1854ee02bSLaurent Vivier /* 2854ee02bSLaurent Vivier * passt network backend 3854ee02bSLaurent Vivier * 4854ee02bSLaurent Vivier * Copyright Red Hat 5854ee02bSLaurent Vivier * 6854ee02bSLaurent Vivier * SPDX-License-Identifier: GPL-2.0-or-later 7854ee02bSLaurent Vivier */ 8854ee02bSLaurent Vivier #include "qemu/osdep.h" 9854ee02bSLaurent Vivier #include <glib/gstdio.h> 10da703b06SLaurent Vivier #include "qemu/error-report.h" 11854ee02bSLaurent Vivier #include <gio/gio.h> 12854ee02bSLaurent Vivier #include "net/net.h" 13854ee02bSLaurent Vivier #include "clients.h" 14854ee02bSLaurent Vivier #include "qapi/error.h" 15854ee02bSLaurent Vivier #include "io/net-listener.h" 16da703b06SLaurent Vivier #include "chardev/char-fe.h" 17da703b06SLaurent Vivier #include "net/vhost_net.h" 18da703b06SLaurent Vivier #include "hw/virtio/vhost.h" 19da703b06SLaurent Vivier #include "hw/virtio/vhost-user.h" 20da703b06SLaurent Vivier #include "standard-headers/linux/virtio_net.h" 21854ee02bSLaurent Vivier #include "stream_data.h" 22854ee02bSLaurent Vivier 23da703b06SLaurent Vivier #ifdef CONFIG_VHOST_USER 24da703b06SLaurent Vivier static const int user_feature_bits[] = { 25da703b06SLaurent Vivier VIRTIO_F_NOTIFY_ON_EMPTY, 26da703b06SLaurent Vivier VIRTIO_F_NOTIFICATION_DATA, 27da703b06SLaurent Vivier VIRTIO_RING_F_INDIRECT_DESC, 28da703b06SLaurent Vivier VIRTIO_RING_F_EVENT_IDX, 29da703b06SLaurent Vivier 30da703b06SLaurent Vivier VIRTIO_F_ANY_LAYOUT, 31da703b06SLaurent Vivier VIRTIO_F_VERSION_1, 32da703b06SLaurent Vivier VIRTIO_NET_F_CSUM, 33da703b06SLaurent Vivier VIRTIO_NET_F_GUEST_CSUM, 34da703b06SLaurent Vivier VIRTIO_NET_F_GSO, 35da703b06SLaurent Vivier VIRTIO_NET_F_GUEST_TSO4, 36da703b06SLaurent Vivier VIRTIO_NET_F_GUEST_TSO6, 37da703b06SLaurent Vivier VIRTIO_NET_F_GUEST_ECN, 38da703b06SLaurent Vivier VIRTIO_NET_F_GUEST_UFO, 39da703b06SLaurent Vivier VIRTIO_NET_F_HOST_TSO4, 40da703b06SLaurent Vivier VIRTIO_NET_F_HOST_TSO6, 41da703b06SLaurent Vivier VIRTIO_NET_F_HOST_ECN, 42da703b06SLaurent Vivier VIRTIO_NET_F_HOST_UFO, 43da703b06SLaurent Vivier VIRTIO_NET_F_MRG_RXBUF, 44da703b06SLaurent Vivier VIRTIO_NET_F_MTU, 45da703b06SLaurent Vivier VIRTIO_F_IOMMU_PLATFORM, 46da703b06SLaurent Vivier VIRTIO_F_RING_PACKED, 47da703b06SLaurent Vivier VIRTIO_F_RING_RESET, 48da703b06SLaurent Vivier VIRTIO_F_IN_ORDER, 49da703b06SLaurent Vivier VIRTIO_NET_F_RSS, 50da703b06SLaurent Vivier VIRTIO_NET_F_RSC_EXT, 51da703b06SLaurent Vivier VIRTIO_NET_F_HASH_REPORT, 52da703b06SLaurent Vivier VIRTIO_NET_F_GUEST_USO4, 53da703b06SLaurent Vivier VIRTIO_NET_F_GUEST_USO6, 54da703b06SLaurent Vivier VIRTIO_NET_F_HOST_USO, 55da703b06SLaurent Vivier 56da703b06SLaurent Vivier /* This bit implies RARP isn't sent by QEMU out of band */ 57da703b06SLaurent Vivier VIRTIO_NET_F_GUEST_ANNOUNCE, 58da703b06SLaurent Vivier 59da703b06SLaurent Vivier VIRTIO_NET_F_MQ, 60da703b06SLaurent Vivier 61da703b06SLaurent Vivier VHOST_INVALID_FEATURE_BIT 62da703b06SLaurent Vivier }; 63da703b06SLaurent Vivier #endif 64da703b06SLaurent Vivier 65854ee02bSLaurent Vivier typedef struct NetPasstState { 66854ee02bSLaurent Vivier NetStreamData data; 67854ee02bSLaurent Vivier GPtrArray *args; 68854ee02bSLaurent Vivier gchar *pidfile; 69854ee02bSLaurent Vivier pid_t pid; 70da703b06SLaurent Vivier #ifdef CONFIG_VHOST_USER 71da703b06SLaurent Vivier /* vhost user */ 72da703b06SLaurent Vivier VhostUserState *vhost_user; 73da703b06SLaurent Vivier VHostNetState *vhost_net; 74da703b06SLaurent Vivier CharBackend vhost_chr; 75da703b06SLaurent Vivier guint vhost_watch; 76da703b06SLaurent Vivier uint64_t acked_features; 77da703b06SLaurent Vivier bool started; 78da703b06SLaurent Vivier #endif 79854ee02bSLaurent Vivier } NetPasstState; 80854ee02bSLaurent Vivier 81854ee02bSLaurent Vivier static int net_passt_stream_start(NetPasstState *s, Error **errp); 82854ee02bSLaurent Vivier 83854ee02bSLaurent Vivier static void net_passt_cleanup(NetClientState *nc) 84854ee02bSLaurent Vivier { 85854ee02bSLaurent Vivier NetPasstState *s = DO_UPCAST(NetPasstState, data.nc, nc); 86854ee02bSLaurent Vivier 87da703b06SLaurent Vivier #ifdef CONFIG_VHOST_USER 88da703b06SLaurent Vivier if (s->vhost_net) { 89da703b06SLaurent Vivier vhost_net_cleanup(s->vhost_net); 90da703b06SLaurent Vivier g_free(s->vhost_net); 91da703b06SLaurent Vivier s->vhost_net = NULL; 92da703b06SLaurent Vivier } 93da703b06SLaurent Vivier if (s->vhost_watch) { 94da703b06SLaurent Vivier g_source_remove(s->vhost_watch); 95da703b06SLaurent Vivier s->vhost_watch = 0; 96da703b06SLaurent Vivier } 97da703b06SLaurent Vivier qemu_chr_fe_deinit(&s->vhost_chr, true); 98da703b06SLaurent Vivier if (s->vhost_user) { 99da703b06SLaurent Vivier vhost_user_cleanup(s->vhost_user); 100da703b06SLaurent Vivier g_free(s->vhost_user); 101da703b06SLaurent Vivier s->vhost_user = NULL; 102da703b06SLaurent Vivier } 103da703b06SLaurent Vivier #endif 104da703b06SLaurent Vivier 105854ee02bSLaurent Vivier kill(s->pid, SIGTERM); 106*f74e4f2eSLaurent Vivier if (g_remove(s->pidfile) != 0) { 107*f74e4f2eSLaurent Vivier warn_report("Failed to remove passt pidfile %s: %s", 108*f74e4f2eSLaurent Vivier s->pidfile, strerror(errno)); 109*f74e4f2eSLaurent Vivier } 110854ee02bSLaurent Vivier g_free(s->pidfile); 111854ee02bSLaurent Vivier g_ptr_array_free(s->args, TRUE); 112854ee02bSLaurent Vivier } 113854ee02bSLaurent Vivier 114854ee02bSLaurent Vivier static ssize_t net_passt_receive(NetClientState *nc, const uint8_t *buf, 115854ee02bSLaurent Vivier size_t size) 116854ee02bSLaurent Vivier { 117854ee02bSLaurent Vivier NetStreamData *d = DO_UPCAST(NetStreamData, nc, nc); 118854ee02bSLaurent Vivier 119854ee02bSLaurent Vivier return net_stream_data_receive(d, buf, size); 120854ee02bSLaurent Vivier } 121854ee02bSLaurent Vivier 122854ee02bSLaurent Vivier static gboolean net_passt_send(QIOChannel *ioc, GIOCondition condition, 123854ee02bSLaurent Vivier gpointer data) 124854ee02bSLaurent Vivier { 125854ee02bSLaurent Vivier if (net_stream_data_send(ioc, condition, data) == G_SOURCE_REMOVE) { 126854ee02bSLaurent Vivier NetPasstState *s = DO_UPCAST(NetPasstState, data, data); 127854ee02bSLaurent Vivier Error *error; 128854ee02bSLaurent Vivier 129854ee02bSLaurent Vivier /* we need to restart passt */ 130854ee02bSLaurent Vivier kill(s->pid, SIGTERM); 131854ee02bSLaurent Vivier if (net_passt_stream_start(s, &error) == -1) { 132854ee02bSLaurent Vivier error_report_err(error); 133854ee02bSLaurent Vivier } 134854ee02bSLaurent Vivier 135854ee02bSLaurent Vivier return G_SOURCE_REMOVE; 136854ee02bSLaurent Vivier } 137854ee02bSLaurent Vivier 138854ee02bSLaurent Vivier return G_SOURCE_CONTINUE; 139854ee02bSLaurent Vivier } 140854ee02bSLaurent Vivier 141da703b06SLaurent Vivier #ifdef CONFIG_VHOST_USER 142da703b06SLaurent Vivier static int passt_set_vnet_endianness(NetClientState *nc, bool enable) 143da703b06SLaurent Vivier { 144da703b06SLaurent Vivier assert(nc->info->type == NET_CLIENT_DRIVER_PASST); 145da703b06SLaurent Vivier 146da703b06SLaurent Vivier return 0; 147da703b06SLaurent Vivier } 148da703b06SLaurent Vivier 149da703b06SLaurent Vivier static bool passt_has_vnet_hdr(NetClientState *nc) 150da703b06SLaurent Vivier { 151da703b06SLaurent Vivier NetPasstState *s = DO_UPCAST(NetPasstState, data.nc, nc); 152da703b06SLaurent Vivier 153da703b06SLaurent Vivier assert(nc->info->type == NET_CLIENT_DRIVER_PASST); 154da703b06SLaurent Vivier 155da703b06SLaurent Vivier return s->vhost_user != NULL; 156da703b06SLaurent Vivier } 157da703b06SLaurent Vivier 158da703b06SLaurent Vivier static bool passt_has_ufo(NetClientState *nc) 159da703b06SLaurent Vivier { 160da703b06SLaurent Vivier NetPasstState *s = DO_UPCAST(NetPasstState, data.nc, nc); 161da703b06SLaurent Vivier 162da703b06SLaurent Vivier assert(nc->info->type == NET_CLIENT_DRIVER_PASST); 163da703b06SLaurent Vivier 164da703b06SLaurent Vivier return s->vhost_user != NULL; 165da703b06SLaurent Vivier } 166da703b06SLaurent Vivier 167da703b06SLaurent Vivier static bool passt_check_peer_type(NetClientState *nc, ObjectClass *oc, 168da703b06SLaurent Vivier Error **errp) 169da703b06SLaurent Vivier { 170da703b06SLaurent Vivier NetPasstState *s = DO_UPCAST(NetPasstState, data.nc, nc); 171da703b06SLaurent Vivier const char *driver = object_class_get_name(oc); 172da703b06SLaurent Vivier 173da703b06SLaurent Vivier assert(nc->info->type == NET_CLIENT_DRIVER_PASST); 174da703b06SLaurent Vivier 175da703b06SLaurent Vivier if (s->vhost_user == NULL) { 176da703b06SLaurent Vivier return true; 177da703b06SLaurent Vivier } 178da703b06SLaurent Vivier 179da703b06SLaurent Vivier if (!g_str_has_prefix(driver, "virtio-net-")) { 180da703b06SLaurent Vivier error_setg(errp, "vhost-user requires frontend driver virtio-net-*"); 181da703b06SLaurent Vivier return false; 182da703b06SLaurent Vivier } 183da703b06SLaurent Vivier 184da703b06SLaurent Vivier return true; 185da703b06SLaurent Vivier } 186da703b06SLaurent Vivier 187da703b06SLaurent Vivier static struct vhost_net *passt_get_vhost_net(NetClientState *nc) 188da703b06SLaurent Vivier { 189da703b06SLaurent Vivier NetPasstState *s = DO_UPCAST(NetPasstState, data.nc, nc); 190da703b06SLaurent Vivier 191da703b06SLaurent Vivier assert(nc->info->type == NET_CLIENT_DRIVER_PASST); 192da703b06SLaurent Vivier 193da703b06SLaurent Vivier return s->vhost_net; 194da703b06SLaurent Vivier } 195da703b06SLaurent Vivier 196da703b06SLaurent Vivier static uint64_t passt_get_acked_features(NetClientState *nc) 197da703b06SLaurent Vivier { 198da703b06SLaurent Vivier NetPasstState *s = DO_UPCAST(NetPasstState, data.nc, nc); 199da703b06SLaurent Vivier 200da703b06SLaurent Vivier assert(nc->info->type == NET_CLIENT_DRIVER_PASST); 201da703b06SLaurent Vivier 202da703b06SLaurent Vivier return s->acked_features; 203da703b06SLaurent Vivier } 204da703b06SLaurent Vivier 205da703b06SLaurent Vivier static void passt_save_acked_features(NetClientState *nc) 206da703b06SLaurent Vivier { 207da703b06SLaurent Vivier NetPasstState *s = DO_UPCAST(NetPasstState, data.nc, nc); 208da703b06SLaurent Vivier 209da703b06SLaurent Vivier assert(nc->info->type == NET_CLIENT_DRIVER_PASST); 210da703b06SLaurent Vivier 211da703b06SLaurent Vivier if (s->vhost_net) { 212da703b06SLaurent Vivier uint64_t features = vhost_net_get_acked_features(s->vhost_net); 213da703b06SLaurent Vivier if (features) { 214da703b06SLaurent Vivier s->acked_features = features; 215da703b06SLaurent Vivier } 216da703b06SLaurent Vivier } 217da703b06SLaurent Vivier } 218da703b06SLaurent Vivier #endif 219da703b06SLaurent Vivier 220854ee02bSLaurent Vivier static NetClientInfo net_passt_info = { 221854ee02bSLaurent Vivier .type = NET_CLIENT_DRIVER_PASST, 222854ee02bSLaurent Vivier .size = sizeof(NetPasstState), 223854ee02bSLaurent Vivier .receive = net_passt_receive, 224854ee02bSLaurent Vivier .cleanup = net_passt_cleanup, 225da703b06SLaurent Vivier #ifdef CONFIG_VHOST_USER 226da703b06SLaurent Vivier .has_vnet_hdr = passt_has_vnet_hdr, 227da703b06SLaurent Vivier .has_ufo = passt_has_ufo, 228da703b06SLaurent Vivier .set_vnet_be = passt_set_vnet_endianness, 229da703b06SLaurent Vivier .set_vnet_le = passt_set_vnet_endianness, 230da703b06SLaurent Vivier .check_peer_type = passt_check_peer_type, 231da703b06SLaurent Vivier .get_vhost_net = passt_get_vhost_net, 232da703b06SLaurent Vivier #endif 233854ee02bSLaurent Vivier }; 234854ee02bSLaurent Vivier 235854ee02bSLaurent Vivier static void net_passt_client_connected(QIOTask *task, gpointer opaque) 236854ee02bSLaurent Vivier { 237854ee02bSLaurent Vivier NetPasstState *s = opaque; 238854ee02bSLaurent Vivier 239854ee02bSLaurent Vivier if (net_stream_data_client_connected(task, &s->data) == 0) { 240854ee02bSLaurent Vivier qemu_set_info_str(&s->data.nc, "stream,connected to pid %d", s->pid); 241854ee02bSLaurent Vivier } 242854ee02bSLaurent Vivier } 243854ee02bSLaurent Vivier 244854ee02bSLaurent Vivier static int net_passt_start_daemon(NetPasstState *s, int sock, Error **errp) 245854ee02bSLaurent Vivier { 246854ee02bSLaurent Vivier g_autoptr(GSubprocess) daemon = NULL; 247854ee02bSLaurent Vivier g_autofree gchar *contents = NULL; 248854ee02bSLaurent Vivier g_autoptr(GError) error = NULL; 249854ee02bSLaurent Vivier GSubprocessLauncher *launcher; 250854ee02bSLaurent Vivier 251854ee02bSLaurent Vivier qemu_set_info_str(&s->data.nc, "launching passt"); 252854ee02bSLaurent Vivier 253854ee02bSLaurent Vivier launcher = g_subprocess_launcher_new(G_SUBPROCESS_FLAGS_NONE); 254854ee02bSLaurent Vivier g_subprocess_launcher_take_fd(launcher, sock, 3); 255854ee02bSLaurent Vivier 256854ee02bSLaurent Vivier daemon = g_subprocess_launcher_spawnv(launcher, 257854ee02bSLaurent Vivier (const gchar *const *)s->args->pdata, 258854ee02bSLaurent Vivier &error); 259854ee02bSLaurent Vivier g_object_unref(launcher); 260854ee02bSLaurent Vivier 261854ee02bSLaurent Vivier if (!daemon) { 262854ee02bSLaurent Vivier error_setg(errp, "Error creating daemon: %s", error->message); 263854ee02bSLaurent Vivier return -1; 264854ee02bSLaurent Vivier } 265854ee02bSLaurent Vivier 266854ee02bSLaurent Vivier if (!g_subprocess_wait(daemon, NULL, &error)) { 267854ee02bSLaurent Vivier error_setg(errp, "Error waiting for daemon: %s", error->message); 268854ee02bSLaurent Vivier return -1; 269854ee02bSLaurent Vivier } 270854ee02bSLaurent Vivier 271854ee02bSLaurent Vivier if (g_subprocess_get_if_exited(daemon) && 272854ee02bSLaurent Vivier g_subprocess_get_exit_status(daemon)) { 273854ee02bSLaurent Vivier return -1; 274854ee02bSLaurent Vivier } 275854ee02bSLaurent Vivier 276854ee02bSLaurent Vivier if (!g_file_get_contents(s->pidfile, &contents, NULL, &error)) { 277854ee02bSLaurent Vivier error_setg(errp, "Cannot read passt pid: %s", error->message); 278854ee02bSLaurent Vivier return -1; 279854ee02bSLaurent Vivier } 280854ee02bSLaurent Vivier 281854ee02bSLaurent Vivier s->pid = (pid_t)g_ascii_strtoll(contents, NULL, 10); 282854ee02bSLaurent Vivier if (s->pid <= 0) { 283854ee02bSLaurent Vivier error_setg(errp, "File '%s' did not contain a valid PID.", s->pidfile); 284854ee02bSLaurent Vivier return -1; 285854ee02bSLaurent Vivier } 286854ee02bSLaurent Vivier 287854ee02bSLaurent Vivier return 0; 288854ee02bSLaurent Vivier } 289854ee02bSLaurent Vivier 290854ee02bSLaurent Vivier static int net_passt_stream_start(NetPasstState *s, Error **errp) 291854ee02bSLaurent Vivier { 292854ee02bSLaurent Vivier QIOChannelSocket *sioc; 293854ee02bSLaurent Vivier SocketAddress *addr; 294854ee02bSLaurent Vivier int sv[2]; 295854ee02bSLaurent Vivier 296854ee02bSLaurent Vivier if (socketpair(PF_UNIX, SOCK_STREAM, 0, sv) == -1) { 297854ee02bSLaurent Vivier error_setg_errno(errp, errno, "socketpair() failed"); 298854ee02bSLaurent Vivier return -1; 299854ee02bSLaurent Vivier } 300854ee02bSLaurent Vivier 301854ee02bSLaurent Vivier /* connect to passt */ 302854ee02bSLaurent Vivier qemu_set_info_str(&s->data.nc, "connecting to passt"); 303854ee02bSLaurent Vivier 304854ee02bSLaurent Vivier /* create socket channel */ 305854ee02bSLaurent Vivier sioc = qio_channel_socket_new(); 306854ee02bSLaurent Vivier s->data.ioc = QIO_CHANNEL(sioc); 307854ee02bSLaurent Vivier s->data.nc.link_down = true; 308854ee02bSLaurent Vivier s->data.send = net_passt_send; 309854ee02bSLaurent Vivier 310854ee02bSLaurent Vivier addr = g_new0(SocketAddress, 1); 311854ee02bSLaurent Vivier addr->type = SOCKET_ADDRESS_TYPE_FD; 312854ee02bSLaurent Vivier addr->u.fd.str = g_strdup_printf("%d", sv[0]); 313854ee02bSLaurent Vivier 314854ee02bSLaurent Vivier qio_channel_socket_connect_async(sioc, addr, 315854ee02bSLaurent Vivier net_passt_client_connected, s, 316854ee02bSLaurent Vivier NULL, NULL); 317854ee02bSLaurent Vivier 318854ee02bSLaurent Vivier qapi_free_SocketAddress(addr); 319854ee02bSLaurent Vivier 320854ee02bSLaurent Vivier /* start passt */ 321854ee02bSLaurent Vivier if (net_passt_start_daemon(s, sv[1], errp) == -1) { 322854ee02bSLaurent Vivier close(sv[0]); 323854ee02bSLaurent Vivier close(sv[1]); 324854ee02bSLaurent Vivier return -1; 325854ee02bSLaurent Vivier } 326854ee02bSLaurent Vivier close(sv[1]); 327854ee02bSLaurent Vivier 328854ee02bSLaurent Vivier return 0; 329854ee02bSLaurent Vivier } 330854ee02bSLaurent Vivier 331da703b06SLaurent Vivier #ifdef CONFIG_VHOST_USER 332da703b06SLaurent Vivier static gboolean passt_vhost_user_watch(void *do_not_use, GIOCondition cond, 333da703b06SLaurent Vivier void *opaque) 334da703b06SLaurent Vivier { 335da703b06SLaurent Vivier NetPasstState *s = opaque; 336da703b06SLaurent Vivier 337da703b06SLaurent Vivier qemu_chr_fe_disconnect(&s->vhost_chr); 338da703b06SLaurent Vivier 339da703b06SLaurent Vivier return G_SOURCE_CONTINUE; 340da703b06SLaurent Vivier } 341da703b06SLaurent Vivier 342da703b06SLaurent Vivier static void passt_vhost_user_event(void *opaque, QEMUChrEvent event); 343da703b06SLaurent Vivier 344da703b06SLaurent Vivier static void chr_closed_bh(void *opaque) 345da703b06SLaurent Vivier { 346da703b06SLaurent Vivier NetPasstState *s = opaque; 347da703b06SLaurent Vivier 348da703b06SLaurent Vivier passt_save_acked_features(&s->data.nc); 349da703b06SLaurent Vivier 350da703b06SLaurent Vivier net_client_set_link(&(NetClientState *){ &s->data.nc }, 1, false); 351da703b06SLaurent Vivier 352da703b06SLaurent Vivier qemu_chr_fe_set_handlers(&s->vhost_chr, NULL, NULL, passt_vhost_user_event, 353da703b06SLaurent Vivier NULL, s, NULL, true); 354da703b06SLaurent Vivier } 355da703b06SLaurent Vivier 356da703b06SLaurent Vivier static void passt_vhost_user_stop(NetPasstState *s) 357da703b06SLaurent Vivier { 358da703b06SLaurent Vivier passt_save_acked_features(&s->data.nc); 359da703b06SLaurent Vivier vhost_net_cleanup(s->vhost_net); 360da703b06SLaurent Vivier } 361da703b06SLaurent Vivier 362da703b06SLaurent Vivier static int passt_vhost_user_start(NetPasstState *s, VhostUserState *be) 363da703b06SLaurent Vivier { 364da703b06SLaurent Vivier struct vhost_net *net = NULL; 365da703b06SLaurent Vivier VhostNetOptions options; 366da703b06SLaurent Vivier 367da703b06SLaurent Vivier options.backend_type = VHOST_BACKEND_TYPE_USER; 368da703b06SLaurent Vivier options.net_backend = &s->data.nc; 369da703b06SLaurent Vivier options.opaque = be; 370da703b06SLaurent Vivier options.busyloop_timeout = 0; 371da703b06SLaurent Vivier options.nvqs = 2; 372da703b06SLaurent Vivier options.feature_bits = user_feature_bits; 373da703b06SLaurent Vivier options.max_tx_queue_size = VIRTQUEUE_MAX_SIZE; 374da703b06SLaurent Vivier options.get_acked_features = passt_get_acked_features; 375da703b06SLaurent Vivier options.save_acked_features = passt_save_acked_features; 376da703b06SLaurent Vivier options.is_vhost_user = true; 377da703b06SLaurent Vivier 378da703b06SLaurent Vivier net = vhost_net_init(&options); 379da703b06SLaurent Vivier if (!net) { 380da703b06SLaurent Vivier error_report("failed to init passt vhost_net"); 381c40ef724SLaurent Vivier passt_vhost_user_stop(s); 382c40ef724SLaurent Vivier return -1; 383da703b06SLaurent Vivier } 384da703b06SLaurent Vivier 385da703b06SLaurent Vivier if (s->vhost_net) { 386da703b06SLaurent Vivier vhost_net_cleanup(s->vhost_net); 387da703b06SLaurent Vivier g_free(s->vhost_net); 388da703b06SLaurent Vivier } 389da703b06SLaurent Vivier s->vhost_net = net; 390da703b06SLaurent Vivier 391da703b06SLaurent Vivier return 0; 392da703b06SLaurent Vivier } 393da703b06SLaurent Vivier 394da703b06SLaurent Vivier static void passt_vhost_user_event(void *opaque, QEMUChrEvent event) 395da703b06SLaurent Vivier { 396da703b06SLaurent Vivier NetPasstState *s = opaque; 397da703b06SLaurent Vivier 398da703b06SLaurent Vivier switch (event) { 399da703b06SLaurent Vivier case CHR_EVENT_OPENED: 400da703b06SLaurent Vivier if (passt_vhost_user_start(s, s->vhost_user) < 0) { 401da703b06SLaurent Vivier qemu_chr_fe_disconnect(&s->vhost_chr); 402da703b06SLaurent Vivier return; 403da703b06SLaurent Vivier } 404da703b06SLaurent Vivier s->vhost_watch = qemu_chr_fe_add_watch(&s->vhost_chr, G_IO_HUP, 405da703b06SLaurent Vivier passt_vhost_user_watch, s); 406da703b06SLaurent Vivier net_client_set_link(&(NetClientState *){ &s->data.nc }, 1, true); 407da703b06SLaurent Vivier s->started = true; 408da703b06SLaurent Vivier break; 409da703b06SLaurent Vivier case CHR_EVENT_CLOSED: 410da703b06SLaurent Vivier if (s->vhost_watch) { 411da703b06SLaurent Vivier AioContext *ctx = qemu_get_current_aio_context(); 412da703b06SLaurent Vivier 413da703b06SLaurent Vivier g_source_remove(s->vhost_watch); 414da703b06SLaurent Vivier s->vhost_watch = 0; 415da703b06SLaurent Vivier qemu_chr_fe_set_handlers(&s->vhost_chr, NULL, NULL, NULL, NULL, 416da703b06SLaurent Vivier NULL, NULL, false); 417da703b06SLaurent Vivier 418da703b06SLaurent Vivier aio_bh_schedule_oneshot(ctx, chr_closed_bh, s); 419da703b06SLaurent Vivier } 420da703b06SLaurent Vivier break; 421da703b06SLaurent Vivier case CHR_EVENT_BREAK: 422da703b06SLaurent Vivier case CHR_EVENT_MUX_IN: 423da703b06SLaurent Vivier case CHR_EVENT_MUX_OUT: 424da703b06SLaurent Vivier /* Ignore */ 425da703b06SLaurent Vivier break; 426da703b06SLaurent Vivier } 427da703b06SLaurent Vivier } 428da703b06SLaurent Vivier 429da703b06SLaurent Vivier static int net_passt_vhost_user_init(NetPasstState *s, Error **errp) 430da703b06SLaurent Vivier { 431da703b06SLaurent Vivier Chardev *chr; 432da703b06SLaurent Vivier int sv[2]; 433da703b06SLaurent Vivier 434da703b06SLaurent Vivier if (socketpair(PF_UNIX, SOCK_STREAM, 0, sv) == -1) { 435da703b06SLaurent Vivier error_setg_errno(errp, errno, "socketpair() failed"); 436da703b06SLaurent Vivier return -1; 437da703b06SLaurent Vivier } 438da703b06SLaurent Vivier 439da703b06SLaurent Vivier /* connect to passt */ 440da703b06SLaurent Vivier qemu_set_info_str(&s->data.nc, "connecting to passt"); 441da703b06SLaurent Vivier 442da703b06SLaurent Vivier /* create chardev */ 443da703b06SLaurent Vivier 444da703b06SLaurent Vivier chr = CHARDEV(object_new(TYPE_CHARDEV_SOCKET)); 445da703b06SLaurent Vivier if (!chr || qemu_chr_add_client(chr, sv[0]) == -1) { 446da703b06SLaurent Vivier object_unref(OBJECT(chr)); 447da703b06SLaurent Vivier error_setg(errp, "Failed to make socket chardev"); 448da703b06SLaurent Vivier goto err; 449da703b06SLaurent Vivier } 450da703b06SLaurent Vivier 451da703b06SLaurent Vivier s->vhost_user = g_new0(struct VhostUserState, 1); 452da703b06SLaurent Vivier if (!qemu_chr_fe_init(&s->vhost_chr, chr, errp) || 453da703b06SLaurent Vivier !vhost_user_init(s->vhost_user, &s->vhost_chr, errp)) { 454da703b06SLaurent Vivier goto err; 455da703b06SLaurent Vivier } 456da703b06SLaurent Vivier 457da703b06SLaurent Vivier /* start passt */ 458da703b06SLaurent Vivier if (net_passt_start_daemon(s, sv[1], errp) == -1) { 459da703b06SLaurent Vivier goto err; 460da703b06SLaurent Vivier } 461da703b06SLaurent Vivier 462da703b06SLaurent Vivier do { 463da703b06SLaurent Vivier if (qemu_chr_fe_wait_connected(&s->vhost_chr, errp) < 0) { 464da703b06SLaurent Vivier goto err; 465da703b06SLaurent Vivier } 466da703b06SLaurent Vivier 467da703b06SLaurent Vivier qemu_chr_fe_set_handlers(&s->vhost_chr, NULL, NULL, 468da703b06SLaurent Vivier passt_vhost_user_event, NULL, s, NULL, 469da703b06SLaurent Vivier true); 470da703b06SLaurent Vivier } while (!s->started); 471da703b06SLaurent Vivier 472da703b06SLaurent Vivier qemu_set_info_str(&s->data.nc, "vhost-user,connected to pid %d", s->pid); 473da703b06SLaurent Vivier 474da703b06SLaurent Vivier close(sv[1]); 475da703b06SLaurent Vivier return 0; 476da703b06SLaurent Vivier err: 477da703b06SLaurent Vivier close(sv[0]); 478da703b06SLaurent Vivier close(sv[1]); 479da703b06SLaurent Vivier 480da703b06SLaurent Vivier return -1; 481da703b06SLaurent Vivier } 482da703b06SLaurent Vivier #else 483da703b06SLaurent Vivier static int net_passt_vhost_user_init(NetPasstState *s, Error **errp) 484da703b06SLaurent Vivier { 485da703b06SLaurent Vivier error_setg(errp, "vhost-user support has not been built"); 486da703b06SLaurent Vivier 487da703b06SLaurent Vivier return -1; 488da703b06SLaurent Vivier } 489da703b06SLaurent Vivier #endif 490da703b06SLaurent Vivier 491854ee02bSLaurent Vivier static GPtrArray *net_passt_decode_args(const NetDevPasstOptions *passt, 492854ee02bSLaurent Vivier gchar *pidfile, Error **errp) 493854ee02bSLaurent Vivier { 494854ee02bSLaurent Vivier GPtrArray *args = g_ptr_array_new_with_free_func(g_free); 495854ee02bSLaurent Vivier 496854ee02bSLaurent Vivier if (passt->path) { 497854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup(passt->path)); 498854ee02bSLaurent Vivier } else { 499854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup("passt")); 500854ee02bSLaurent Vivier } 501854ee02bSLaurent Vivier 502da703b06SLaurent Vivier if (passt->has_vhost_user && passt->vhost_user) { 503da703b06SLaurent Vivier g_ptr_array_add(args, g_strdup("--vhost-user")); 504da703b06SLaurent Vivier } 505da703b06SLaurent Vivier 506854ee02bSLaurent Vivier /* by default, be quiet */ 507854ee02bSLaurent Vivier if (!passt->has_quiet || passt->quiet) { 508854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup("--quiet")); 509854ee02bSLaurent Vivier } 510854ee02bSLaurent Vivier 511854ee02bSLaurent Vivier if (passt->has_mtu) { 512854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup("--mtu")); 513854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup_printf("%"PRId64, passt->mtu)); 514854ee02bSLaurent Vivier } 515854ee02bSLaurent Vivier 516854ee02bSLaurent Vivier if (passt->address) { 517854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup("--address")); 518854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup(passt->address)); 519854ee02bSLaurent Vivier } 520854ee02bSLaurent Vivier 521854ee02bSLaurent Vivier if (passt->netmask) { 522854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup("--netmask")); 523854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup(passt->netmask)); 524854ee02bSLaurent Vivier } 525854ee02bSLaurent Vivier 526854ee02bSLaurent Vivier if (passt->mac) { 527854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup("--mac-addr")); 528854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup(passt->mac)); 529854ee02bSLaurent Vivier } 530854ee02bSLaurent Vivier 531854ee02bSLaurent Vivier if (passt->gateway) { 532854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup("--gateway")); 533854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup(passt->gateway)); 534854ee02bSLaurent Vivier } 535854ee02bSLaurent Vivier 536854ee02bSLaurent Vivier if (passt->interface) { 537854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup("--interface")); 538854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup(passt->interface)); 539854ee02bSLaurent Vivier } 540854ee02bSLaurent Vivier 541854ee02bSLaurent Vivier if (passt->outbound) { 542854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup("--outbound")); 543854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup(passt->outbound)); 544854ee02bSLaurent Vivier } 545854ee02bSLaurent Vivier 546854ee02bSLaurent Vivier if (passt->outbound_if4) { 547854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup("--outbound-if4")); 548854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup(passt->outbound_if4)); 549854ee02bSLaurent Vivier } 550854ee02bSLaurent Vivier 551854ee02bSLaurent Vivier if (passt->outbound_if6) { 552854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup("--outbound-if6")); 553854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup(passt->outbound_if6)); 554854ee02bSLaurent Vivier } 555854ee02bSLaurent Vivier 556854ee02bSLaurent Vivier if (passt->dns) { 557854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup("--dns")); 558854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup(passt->dns)); 559854ee02bSLaurent Vivier } 560854ee02bSLaurent Vivier if (passt->fqdn) { 561854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup("--fqdn")); 562854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup(passt->fqdn)); 563854ee02bSLaurent Vivier } 564854ee02bSLaurent Vivier 565854ee02bSLaurent Vivier if (passt->has_dhcp_dns && !passt->dhcp_dns) { 566854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup("--no-dhcp-dns")); 567854ee02bSLaurent Vivier } 568854ee02bSLaurent Vivier 569854ee02bSLaurent Vivier if (passt->has_dhcp_search && !passt->dhcp_search) { 570854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup("--no-dhcp-search")); 571854ee02bSLaurent Vivier } 572854ee02bSLaurent Vivier 573854ee02bSLaurent Vivier if (passt->map_host_loopback) { 574854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup("--map-host-loopback")); 575854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup(passt->map_host_loopback)); 576854ee02bSLaurent Vivier } 577854ee02bSLaurent Vivier 578854ee02bSLaurent Vivier if (passt->map_guest_addr) { 579854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup("--map-guest-addr")); 580854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup(passt->map_guest_addr)); 581854ee02bSLaurent Vivier } 582854ee02bSLaurent Vivier 583854ee02bSLaurent Vivier if (passt->dns_forward) { 584854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup("--dns-forward")); 585854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup(passt->dns_forward)); 586854ee02bSLaurent Vivier } 587854ee02bSLaurent Vivier 588854ee02bSLaurent Vivier if (passt->dns_host) { 589854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup("--dns-host")); 590854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup(passt->dns_host)); 591854ee02bSLaurent Vivier } 592854ee02bSLaurent Vivier 593854ee02bSLaurent Vivier if (passt->has_tcp && !passt->tcp) { 594854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup("--no-tcp")); 595854ee02bSLaurent Vivier } 596854ee02bSLaurent Vivier 597854ee02bSLaurent Vivier if (passt->has_udp && !passt->udp) { 598854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup("--no-udp")); 599854ee02bSLaurent Vivier } 600854ee02bSLaurent Vivier 601854ee02bSLaurent Vivier if (passt->has_icmp && !passt->icmp) { 602854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup("--no-icmp")); 603854ee02bSLaurent Vivier } 604854ee02bSLaurent Vivier 605854ee02bSLaurent Vivier if (passt->has_dhcp && !passt->dhcp) { 606854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup("--no-dhcp")); 607854ee02bSLaurent Vivier } 608854ee02bSLaurent Vivier 609854ee02bSLaurent Vivier if (passt->has_ndp && !passt->ndp) { 610854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup("--no-ndp")); 611854ee02bSLaurent Vivier } 612854ee02bSLaurent Vivier if (passt->has_dhcpv6 && !passt->dhcpv6) { 613854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup("--no-dhcpv6")); 614854ee02bSLaurent Vivier } 615854ee02bSLaurent Vivier 616854ee02bSLaurent Vivier if (passt->has_ra && !passt->ra) { 617854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup("--no-ra")); 618854ee02bSLaurent Vivier } 619854ee02bSLaurent Vivier 620854ee02bSLaurent Vivier if (passt->has_freebind && passt->freebind) { 621854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup("--freebind")); 622854ee02bSLaurent Vivier } 623854ee02bSLaurent Vivier 624854ee02bSLaurent Vivier if (passt->has_ipv4 && !passt->ipv4) { 625854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup("--ipv6-only")); 626854ee02bSLaurent Vivier } 627854ee02bSLaurent Vivier 628854ee02bSLaurent Vivier if (passt->has_ipv6 && !passt->ipv6) { 629854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup("--ipv4-only")); 630854ee02bSLaurent Vivier } 631854ee02bSLaurent Vivier 632854ee02bSLaurent Vivier if (passt->has_search && passt->search) { 633854ee02bSLaurent Vivier const StringList *list = passt->search; 634854ee02bSLaurent Vivier GString *domains = g_string_new(list->value->str); 635854ee02bSLaurent Vivier 636854ee02bSLaurent Vivier list = list->next; 637854ee02bSLaurent Vivier while (list) { 638854ee02bSLaurent Vivier g_string_append(domains, " "); 639854ee02bSLaurent Vivier g_string_append(domains, list->value->str); 640854ee02bSLaurent Vivier list = list->next; 641854ee02bSLaurent Vivier } 642854ee02bSLaurent Vivier 643854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup("--search")); 644854ee02bSLaurent Vivier g_ptr_array_add(args, g_string_free(domains, FALSE)); 645854ee02bSLaurent Vivier } 646854ee02bSLaurent Vivier 647854ee02bSLaurent Vivier if (passt->has_tcp_ports && passt->tcp_ports) { 648854ee02bSLaurent Vivier const StringList *list = passt->tcp_ports; 649854ee02bSLaurent Vivier GString *tcp_ports = g_string_new(list->value->str); 650854ee02bSLaurent Vivier 651854ee02bSLaurent Vivier list = list->next; 652854ee02bSLaurent Vivier while (list) { 653854ee02bSLaurent Vivier g_string_append(tcp_ports, ","); 654854ee02bSLaurent Vivier g_string_append(tcp_ports, list->value->str); 655854ee02bSLaurent Vivier list = list->next; 656854ee02bSLaurent Vivier } 657854ee02bSLaurent Vivier 658854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup("--tcp-ports")); 659854ee02bSLaurent Vivier g_ptr_array_add(args, g_string_free(tcp_ports, FALSE)); 660854ee02bSLaurent Vivier } 661854ee02bSLaurent Vivier 662854ee02bSLaurent Vivier if (passt->has_udp_ports && passt->udp_ports) { 663854ee02bSLaurent Vivier const StringList *list = passt->udp_ports; 664854ee02bSLaurent Vivier GString *udp_ports = g_string_new(list->value->str); 665854ee02bSLaurent Vivier 666854ee02bSLaurent Vivier list = list->next; 667854ee02bSLaurent Vivier while (list) { 668854ee02bSLaurent Vivier g_string_append(udp_ports, ","); 669854ee02bSLaurent Vivier g_string_append(udp_ports, list->value->str); 670854ee02bSLaurent Vivier list = list->next; 671854ee02bSLaurent Vivier } 672854ee02bSLaurent Vivier 673854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup("--udp-ports")); 674854ee02bSLaurent Vivier g_ptr_array_add(args, g_string_free(udp_ports, FALSE)); 675854ee02bSLaurent Vivier } 676854ee02bSLaurent Vivier 677854ee02bSLaurent Vivier if (passt->has_param && passt->param) { 678854ee02bSLaurent Vivier const StringList *list = passt->param; 679854ee02bSLaurent Vivier 680854ee02bSLaurent Vivier while (list) { 681854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup(list->value->str)); 682854ee02bSLaurent Vivier list = list->next; 683854ee02bSLaurent Vivier } 684854ee02bSLaurent Vivier } 685854ee02bSLaurent Vivier 686854ee02bSLaurent Vivier /* provide a pid file to be able to kil passt on exit */ 687854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup("--pid")); 688854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup(pidfile)); 689854ee02bSLaurent Vivier 690854ee02bSLaurent Vivier /* g_subprocess_launcher_take_fd() will set the socket on fd 3 */ 691854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup("--fd")); 692854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup("3")); 693854ee02bSLaurent Vivier 694854ee02bSLaurent Vivier g_ptr_array_add(args, NULL); 695854ee02bSLaurent Vivier 696854ee02bSLaurent Vivier return args; 697854ee02bSLaurent Vivier } 698854ee02bSLaurent Vivier 699854ee02bSLaurent Vivier int net_init_passt(const Netdev *netdev, const char *name, 700854ee02bSLaurent Vivier NetClientState *peer, Error **errp) 701854ee02bSLaurent Vivier { 702854ee02bSLaurent Vivier g_autoptr(GError) error = NULL; 703854ee02bSLaurent Vivier NetClientState *nc; 704854ee02bSLaurent Vivier NetPasstState *s; 705854ee02bSLaurent Vivier GPtrArray *args; 706854ee02bSLaurent Vivier gchar *pidfile; 707854ee02bSLaurent Vivier int pidfd; 708854ee02bSLaurent Vivier 709854ee02bSLaurent Vivier assert(netdev->type == NET_CLIENT_DRIVER_PASST); 710854ee02bSLaurent Vivier 711854ee02bSLaurent Vivier pidfd = g_file_open_tmp("passt-XXXXXX.pid", &pidfile, &error); 712854ee02bSLaurent Vivier if (pidfd == -1) { 713854ee02bSLaurent Vivier error_setg(errp, "Failed to create temporary file: %s", error->message); 714854ee02bSLaurent Vivier return -1; 715854ee02bSLaurent Vivier } 716854ee02bSLaurent Vivier close(pidfd); 717854ee02bSLaurent Vivier 718854ee02bSLaurent Vivier args = net_passt_decode_args(&netdev->u.passt, pidfile, errp); 719854ee02bSLaurent Vivier if (args == NULL) { 720854ee02bSLaurent Vivier g_free(pidfile); 721854ee02bSLaurent Vivier return -1; 722854ee02bSLaurent Vivier } 723854ee02bSLaurent Vivier 724854ee02bSLaurent Vivier nc = qemu_new_net_client(&net_passt_info, peer, "passt", name); 725854ee02bSLaurent Vivier s = DO_UPCAST(NetPasstState, data.nc, nc); 726854ee02bSLaurent Vivier 727854ee02bSLaurent Vivier s->args = args; 728854ee02bSLaurent Vivier s->pidfile = pidfile; 729854ee02bSLaurent Vivier 730da703b06SLaurent Vivier if (netdev->u.passt.has_vhost_user && netdev->u.passt.vhost_user) { 731da703b06SLaurent Vivier if (net_passt_vhost_user_init(s, errp) == -1) { 732da703b06SLaurent Vivier qemu_del_net_client(nc); 733da703b06SLaurent Vivier return -1; 734da703b06SLaurent Vivier } 735da703b06SLaurent Vivier 736da703b06SLaurent Vivier return 0; 737da703b06SLaurent Vivier } 738da703b06SLaurent Vivier 739854ee02bSLaurent Vivier if (net_passt_stream_start(s, errp) == -1) { 740854ee02bSLaurent Vivier qemu_del_net_client(nc); 741854ee02bSLaurent Vivier return -1; 742854ee02bSLaurent Vivier } 743854ee02bSLaurent Vivier 744854ee02bSLaurent Vivier return 0; 745854ee02bSLaurent Vivier } 746