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); 106854ee02bSLaurent Vivier g_remove(s->pidfile); 107854ee02bSLaurent Vivier g_free(s->pidfile); 108854ee02bSLaurent Vivier g_ptr_array_free(s->args, TRUE); 109854ee02bSLaurent Vivier } 110854ee02bSLaurent Vivier 111854ee02bSLaurent Vivier static ssize_t net_passt_receive(NetClientState *nc, const uint8_t *buf, 112854ee02bSLaurent Vivier size_t size) 113854ee02bSLaurent Vivier { 114854ee02bSLaurent Vivier NetStreamData *d = DO_UPCAST(NetStreamData, nc, nc); 115854ee02bSLaurent Vivier 116854ee02bSLaurent Vivier return net_stream_data_receive(d, buf, size); 117854ee02bSLaurent Vivier } 118854ee02bSLaurent Vivier 119854ee02bSLaurent Vivier static gboolean net_passt_send(QIOChannel *ioc, GIOCondition condition, 120854ee02bSLaurent Vivier gpointer data) 121854ee02bSLaurent Vivier { 122854ee02bSLaurent Vivier if (net_stream_data_send(ioc, condition, data) == G_SOURCE_REMOVE) { 123854ee02bSLaurent Vivier NetPasstState *s = DO_UPCAST(NetPasstState, data, data); 124854ee02bSLaurent Vivier Error *error; 125854ee02bSLaurent Vivier 126854ee02bSLaurent Vivier /* we need to restart passt */ 127854ee02bSLaurent Vivier kill(s->pid, SIGTERM); 128854ee02bSLaurent Vivier if (net_passt_stream_start(s, &error) == -1) { 129854ee02bSLaurent Vivier error_report_err(error); 130854ee02bSLaurent Vivier } 131854ee02bSLaurent Vivier 132854ee02bSLaurent Vivier return G_SOURCE_REMOVE; 133854ee02bSLaurent Vivier } 134854ee02bSLaurent Vivier 135854ee02bSLaurent Vivier return G_SOURCE_CONTINUE; 136854ee02bSLaurent Vivier } 137854ee02bSLaurent Vivier 138da703b06SLaurent Vivier #ifdef CONFIG_VHOST_USER 139da703b06SLaurent Vivier static int passt_set_vnet_endianness(NetClientState *nc, bool enable) 140da703b06SLaurent Vivier { 141da703b06SLaurent Vivier assert(nc->info->type == NET_CLIENT_DRIVER_PASST); 142da703b06SLaurent Vivier 143da703b06SLaurent Vivier return 0; 144da703b06SLaurent Vivier } 145da703b06SLaurent Vivier 146da703b06SLaurent Vivier static bool passt_has_vnet_hdr(NetClientState *nc) 147da703b06SLaurent Vivier { 148da703b06SLaurent Vivier NetPasstState *s = DO_UPCAST(NetPasstState, data.nc, nc); 149da703b06SLaurent Vivier 150da703b06SLaurent Vivier assert(nc->info->type == NET_CLIENT_DRIVER_PASST); 151da703b06SLaurent Vivier 152da703b06SLaurent Vivier return s->vhost_user != NULL; 153da703b06SLaurent Vivier } 154da703b06SLaurent Vivier 155da703b06SLaurent Vivier static bool passt_has_ufo(NetClientState *nc) 156da703b06SLaurent Vivier { 157da703b06SLaurent Vivier NetPasstState *s = DO_UPCAST(NetPasstState, data.nc, nc); 158da703b06SLaurent Vivier 159da703b06SLaurent Vivier assert(nc->info->type == NET_CLIENT_DRIVER_PASST); 160da703b06SLaurent Vivier 161da703b06SLaurent Vivier return s->vhost_user != NULL; 162da703b06SLaurent Vivier } 163da703b06SLaurent Vivier 164da703b06SLaurent Vivier static bool passt_check_peer_type(NetClientState *nc, ObjectClass *oc, 165da703b06SLaurent Vivier Error **errp) 166da703b06SLaurent Vivier { 167da703b06SLaurent Vivier NetPasstState *s = DO_UPCAST(NetPasstState, data.nc, nc); 168da703b06SLaurent Vivier const char *driver = object_class_get_name(oc); 169da703b06SLaurent Vivier 170da703b06SLaurent Vivier assert(nc->info->type == NET_CLIENT_DRIVER_PASST); 171da703b06SLaurent Vivier 172da703b06SLaurent Vivier if (s->vhost_user == NULL) { 173da703b06SLaurent Vivier return true; 174da703b06SLaurent Vivier } 175da703b06SLaurent Vivier 176da703b06SLaurent Vivier if (!g_str_has_prefix(driver, "virtio-net-")) { 177da703b06SLaurent Vivier error_setg(errp, "vhost-user requires frontend driver virtio-net-*"); 178da703b06SLaurent Vivier return false; 179da703b06SLaurent Vivier } 180da703b06SLaurent Vivier 181da703b06SLaurent Vivier return true; 182da703b06SLaurent Vivier } 183da703b06SLaurent Vivier 184da703b06SLaurent Vivier static struct vhost_net *passt_get_vhost_net(NetClientState *nc) 185da703b06SLaurent Vivier { 186da703b06SLaurent Vivier NetPasstState *s = DO_UPCAST(NetPasstState, data.nc, nc); 187da703b06SLaurent Vivier 188da703b06SLaurent Vivier assert(nc->info->type == NET_CLIENT_DRIVER_PASST); 189da703b06SLaurent Vivier 190da703b06SLaurent Vivier return s->vhost_net; 191da703b06SLaurent Vivier } 192da703b06SLaurent Vivier 193da703b06SLaurent Vivier static uint64_t passt_get_acked_features(NetClientState *nc) 194da703b06SLaurent Vivier { 195da703b06SLaurent Vivier NetPasstState *s = DO_UPCAST(NetPasstState, data.nc, nc); 196da703b06SLaurent Vivier 197da703b06SLaurent Vivier assert(nc->info->type == NET_CLIENT_DRIVER_PASST); 198da703b06SLaurent Vivier 199da703b06SLaurent Vivier return s->acked_features; 200da703b06SLaurent Vivier } 201da703b06SLaurent Vivier 202da703b06SLaurent Vivier static void passt_save_acked_features(NetClientState *nc) 203da703b06SLaurent Vivier { 204da703b06SLaurent Vivier NetPasstState *s = DO_UPCAST(NetPasstState, data.nc, nc); 205da703b06SLaurent Vivier 206da703b06SLaurent Vivier assert(nc->info->type == NET_CLIENT_DRIVER_PASST); 207da703b06SLaurent Vivier 208da703b06SLaurent Vivier if (s->vhost_net) { 209da703b06SLaurent Vivier uint64_t features = vhost_net_get_acked_features(s->vhost_net); 210da703b06SLaurent Vivier if (features) { 211da703b06SLaurent Vivier s->acked_features = features; 212da703b06SLaurent Vivier } 213da703b06SLaurent Vivier } 214da703b06SLaurent Vivier } 215da703b06SLaurent Vivier #endif 216da703b06SLaurent Vivier 217854ee02bSLaurent Vivier static NetClientInfo net_passt_info = { 218854ee02bSLaurent Vivier .type = NET_CLIENT_DRIVER_PASST, 219854ee02bSLaurent Vivier .size = sizeof(NetPasstState), 220854ee02bSLaurent Vivier .receive = net_passt_receive, 221854ee02bSLaurent Vivier .cleanup = net_passt_cleanup, 222da703b06SLaurent Vivier #ifdef CONFIG_VHOST_USER 223da703b06SLaurent Vivier .has_vnet_hdr = passt_has_vnet_hdr, 224da703b06SLaurent Vivier .has_ufo = passt_has_ufo, 225da703b06SLaurent Vivier .set_vnet_be = passt_set_vnet_endianness, 226da703b06SLaurent Vivier .set_vnet_le = passt_set_vnet_endianness, 227da703b06SLaurent Vivier .check_peer_type = passt_check_peer_type, 228da703b06SLaurent Vivier .get_vhost_net = passt_get_vhost_net, 229da703b06SLaurent Vivier #endif 230854ee02bSLaurent Vivier }; 231854ee02bSLaurent Vivier 232854ee02bSLaurent Vivier static void net_passt_client_connected(QIOTask *task, gpointer opaque) 233854ee02bSLaurent Vivier { 234854ee02bSLaurent Vivier NetPasstState *s = opaque; 235854ee02bSLaurent Vivier 236854ee02bSLaurent Vivier if (net_stream_data_client_connected(task, &s->data) == 0) { 237854ee02bSLaurent Vivier qemu_set_info_str(&s->data.nc, "stream,connected to pid %d", s->pid); 238854ee02bSLaurent Vivier } 239854ee02bSLaurent Vivier } 240854ee02bSLaurent Vivier 241854ee02bSLaurent Vivier static int net_passt_start_daemon(NetPasstState *s, int sock, Error **errp) 242854ee02bSLaurent Vivier { 243854ee02bSLaurent Vivier g_autoptr(GSubprocess) daemon = NULL; 244854ee02bSLaurent Vivier g_autofree gchar *contents = NULL; 245854ee02bSLaurent Vivier g_autoptr(GError) error = NULL; 246854ee02bSLaurent Vivier GSubprocessLauncher *launcher; 247854ee02bSLaurent Vivier 248854ee02bSLaurent Vivier qemu_set_info_str(&s->data.nc, "launching passt"); 249854ee02bSLaurent Vivier 250854ee02bSLaurent Vivier launcher = g_subprocess_launcher_new(G_SUBPROCESS_FLAGS_NONE); 251854ee02bSLaurent Vivier g_subprocess_launcher_take_fd(launcher, sock, 3); 252854ee02bSLaurent Vivier 253854ee02bSLaurent Vivier daemon = g_subprocess_launcher_spawnv(launcher, 254854ee02bSLaurent Vivier (const gchar *const *)s->args->pdata, 255854ee02bSLaurent Vivier &error); 256854ee02bSLaurent Vivier g_object_unref(launcher); 257854ee02bSLaurent Vivier 258854ee02bSLaurent Vivier if (!daemon) { 259854ee02bSLaurent Vivier error_setg(errp, "Error creating daemon: %s", error->message); 260854ee02bSLaurent Vivier return -1; 261854ee02bSLaurent Vivier } 262854ee02bSLaurent Vivier 263854ee02bSLaurent Vivier if (!g_subprocess_wait(daemon, NULL, &error)) { 264854ee02bSLaurent Vivier error_setg(errp, "Error waiting for daemon: %s", error->message); 265854ee02bSLaurent Vivier return -1; 266854ee02bSLaurent Vivier } 267854ee02bSLaurent Vivier 268854ee02bSLaurent Vivier if (g_subprocess_get_if_exited(daemon) && 269854ee02bSLaurent Vivier g_subprocess_get_exit_status(daemon)) { 270854ee02bSLaurent Vivier return -1; 271854ee02bSLaurent Vivier } 272854ee02bSLaurent Vivier 273854ee02bSLaurent Vivier if (!g_file_get_contents(s->pidfile, &contents, NULL, &error)) { 274854ee02bSLaurent Vivier error_setg(errp, "Cannot read passt pid: %s", error->message); 275854ee02bSLaurent Vivier return -1; 276854ee02bSLaurent Vivier } 277854ee02bSLaurent Vivier 278854ee02bSLaurent Vivier s->pid = (pid_t)g_ascii_strtoll(contents, NULL, 10); 279854ee02bSLaurent Vivier if (s->pid <= 0) { 280854ee02bSLaurent Vivier error_setg(errp, "File '%s' did not contain a valid PID.", s->pidfile); 281854ee02bSLaurent Vivier return -1; 282854ee02bSLaurent Vivier } 283854ee02bSLaurent Vivier 284854ee02bSLaurent Vivier return 0; 285854ee02bSLaurent Vivier } 286854ee02bSLaurent Vivier 287854ee02bSLaurent Vivier static int net_passt_stream_start(NetPasstState *s, Error **errp) 288854ee02bSLaurent Vivier { 289854ee02bSLaurent Vivier QIOChannelSocket *sioc; 290854ee02bSLaurent Vivier SocketAddress *addr; 291854ee02bSLaurent Vivier int sv[2]; 292854ee02bSLaurent Vivier 293854ee02bSLaurent Vivier if (socketpair(PF_UNIX, SOCK_STREAM, 0, sv) == -1) { 294854ee02bSLaurent Vivier error_setg_errno(errp, errno, "socketpair() failed"); 295854ee02bSLaurent Vivier return -1; 296854ee02bSLaurent Vivier } 297854ee02bSLaurent Vivier 298854ee02bSLaurent Vivier /* connect to passt */ 299854ee02bSLaurent Vivier qemu_set_info_str(&s->data.nc, "connecting to passt"); 300854ee02bSLaurent Vivier 301854ee02bSLaurent Vivier /* create socket channel */ 302854ee02bSLaurent Vivier sioc = qio_channel_socket_new(); 303854ee02bSLaurent Vivier s->data.ioc = QIO_CHANNEL(sioc); 304854ee02bSLaurent Vivier s->data.nc.link_down = true; 305854ee02bSLaurent Vivier s->data.send = net_passt_send; 306854ee02bSLaurent Vivier 307854ee02bSLaurent Vivier addr = g_new0(SocketAddress, 1); 308854ee02bSLaurent Vivier addr->type = SOCKET_ADDRESS_TYPE_FD; 309854ee02bSLaurent Vivier addr->u.fd.str = g_strdup_printf("%d", sv[0]); 310854ee02bSLaurent Vivier 311854ee02bSLaurent Vivier qio_channel_socket_connect_async(sioc, addr, 312854ee02bSLaurent Vivier net_passt_client_connected, s, 313854ee02bSLaurent Vivier NULL, NULL); 314854ee02bSLaurent Vivier 315854ee02bSLaurent Vivier qapi_free_SocketAddress(addr); 316854ee02bSLaurent Vivier 317854ee02bSLaurent Vivier /* start passt */ 318854ee02bSLaurent Vivier if (net_passt_start_daemon(s, sv[1], errp) == -1) { 319854ee02bSLaurent Vivier close(sv[0]); 320854ee02bSLaurent Vivier close(sv[1]); 321854ee02bSLaurent Vivier return -1; 322854ee02bSLaurent Vivier } 323854ee02bSLaurent Vivier close(sv[1]); 324854ee02bSLaurent Vivier 325854ee02bSLaurent Vivier return 0; 326854ee02bSLaurent Vivier } 327854ee02bSLaurent Vivier 328da703b06SLaurent Vivier #ifdef CONFIG_VHOST_USER 329da703b06SLaurent Vivier static gboolean passt_vhost_user_watch(void *do_not_use, GIOCondition cond, 330da703b06SLaurent Vivier void *opaque) 331da703b06SLaurent Vivier { 332da703b06SLaurent Vivier NetPasstState *s = opaque; 333da703b06SLaurent Vivier 334da703b06SLaurent Vivier qemu_chr_fe_disconnect(&s->vhost_chr); 335da703b06SLaurent Vivier 336da703b06SLaurent Vivier return G_SOURCE_CONTINUE; 337da703b06SLaurent Vivier } 338da703b06SLaurent Vivier 339da703b06SLaurent Vivier static void passt_vhost_user_event(void *opaque, QEMUChrEvent event); 340da703b06SLaurent Vivier 341da703b06SLaurent Vivier static void chr_closed_bh(void *opaque) 342da703b06SLaurent Vivier { 343da703b06SLaurent Vivier NetPasstState *s = opaque; 344da703b06SLaurent Vivier 345da703b06SLaurent Vivier passt_save_acked_features(&s->data.nc); 346da703b06SLaurent Vivier 347da703b06SLaurent Vivier net_client_set_link(&(NetClientState *){ &s->data.nc }, 1, false); 348da703b06SLaurent Vivier 349da703b06SLaurent Vivier qemu_chr_fe_set_handlers(&s->vhost_chr, NULL, NULL, passt_vhost_user_event, 350da703b06SLaurent Vivier NULL, s, NULL, true); 351da703b06SLaurent Vivier } 352da703b06SLaurent Vivier 353da703b06SLaurent Vivier static void passt_vhost_user_stop(NetPasstState *s) 354da703b06SLaurent Vivier { 355da703b06SLaurent Vivier passt_save_acked_features(&s->data.nc); 356da703b06SLaurent Vivier vhost_net_cleanup(s->vhost_net); 357da703b06SLaurent Vivier } 358da703b06SLaurent Vivier 359da703b06SLaurent Vivier static int passt_vhost_user_start(NetPasstState *s, VhostUserState *be) 360da703b06SLaurent Vivier { 361da703b06SLaurent Vivier struct vhost_net *net = NULL; 362da703b06SLaurent Vivier VhostNetOptions options; 363da703b06SLaurent Vivier 364da703b06SLaurent Vivier options.backend_type = VHOST_BACKEND_TYPE_USER; 365da703b06SLaurent Vivier options.net_backend = &s->data.nc; 366da703b06SLaurent Vivier options.opaque = be; 367da703b06SLaurent Vivier options.busyloop_timeout = 0; 368da703b06SLaurent Vivier options.nvqs = 2; 369da703b06SLaurent Vivier options.feature_bits = user_feature_bits; 370da703b06SLaurent Vivier options.max_tx_queue_size = VIRTQUEUE_MAX_SIZE; 371da703b06SLaurent Vivier options.get_acked_features = passt_get_acked_features; 372da703b06SLaurent Vivier options.save_acked_features = passt_save_acked_features; 373da703b06SLaurent Vivier options.is_vhost_user = true; 374da703b06SLaurent Vivier 375da703b06SLaurent Vivier net = vhost_net_init(&options); 376da703b06SLaurent Vivier if (!net) { 377da703b06SLaurent Vivier error_report("failed to init passt vhost_net"); 378*c40ef724SLaurent Vivier passt_vhost_user_stop(s); 379*c40ef724SLaurent Vivier return -1; 380da703b06SLaurent Vivier } 381da703b06SLaurent Vivier 382da703b06SLaurent Vivier if (s->vhost_net) { 383da703b06SLaurent Vivier vhost_net_cleanup(s->vhost_net); 384da703b06SLaurent Vivier g_free(s->vhost_net); 385da703b06SLaurent Vivier } 386da703b06SLaurent Vivier s->vhost_net = net; 387da703b06SLaurent Vivier 388da703b06SLaurent Vivier return 0; 389da703b06SLaurent Vivier } 390da703b06SLaurent Vivier 391da703b06SLaurent Vivier static void passt_vhost_user_event(void *opaque, QEMUChrEvent event) 392da703b06SLaurent Vivier { 393da703b06SLaurent Vivier NetPasstState *s = opaque; 394da703b06SLaurent Vivier 395da703b06SLaurent Vivier switch (event) { 396da703b06SLaurent Vivier case CHR_EVENT_OPENED: 397da703b06SLaurent Vivier if (passt_vhost_user_start(s, s->vhost_user) < 0) { 398da703b06SLaurent Vivier qemu_chr_fe_disconnect(&s->vhost_chr); 399da703b06SLaurent Vivier return; 400da703b06SLaurent Vivier } 401da703b06SLaurent Vivier s->vhost_watch = qemu_chr_fe_add_watch(&s->vhost_chr, G_IO_HUP, 402da703b06SLaurent Vivier passt_vhost_user_watch, s); 403da703b06SLaurent Vivier net_client_set_link(&(NetClientState *){ &s->data.nc }, 1, true); 404da703b06SLaurent Vivier s->started = true; 405da703b06SLaurent Vivier break; 406da703b06SLaurent Vivier case CHR_EVENT_CLOSED: 407da703b06SLaurent Vivier if (s->vhost_watch) { 408da703b06SLaurent Vivier AioContext *ctx = qemu_get_current_aio_context(); 409da703b06SLaurent Vivier 410da703b06SLaurent Vivier g_source_remove(s->vhost_watch); 411da703b06SLaurent Vivier s->vhost_watch = 0; 412da703b06SLaurent Vivier qemu_chr_fe_set_handlers(&s->vhost_chr, NULL, NULL, NULL, NULL, 413da703b06SLaurent Vivier NULL, NULL, false); 414da703b06SLaurent Vivier 415da703b06SLaurent Vivier aio_bh_schedule_oneshot(ctx, chr_closed_bh, s); 416da703b06SLaurent Vivier } 417da703b06SLaurent Vivier break; 418da703b06SLaurent Vivier case CHR_EVENT_BREAK: 419da703b06SLaurent Vivier case CHR_EVENT_MUX_IN: 420da703b06SLaurent Vivier case CHR_EVENT_MUX_OUT: 421da703b06SLaurent Vivier /* Ignore */ 422da703b06SLaurent Vivier break; 423da703b06SLaurent Vivier } 424da703b06SLaurent Vivier } 425da703b06SLaurent Vivier 426da703b06SLaurent Vivier static int net_passt_vhost_user_init(NetPasstState *s, Error **errp) 427da703b06SLaurent Vivier { 428da703b06SLaurent Vivier Chardev *chr; 429da703b06SLaurent Vivier int sv[2]; 430da703b06SLaurent Vivier 431da703b06SLaurent Vivier if (socketpair(PF_UNIX, SOCK_STREAM, 0, sv) == -1) { 432da703b06SLaurent Vivier error_setg_errno(errp, errno, "socketpair() failed"); 433da703b06SLaurent Vivier return -1; 434da703b06SLaurent Vivier } 435da703b06SLaurent Vivier 436da703b06SLaurent Vivier /* connect to passt */ 437da703b06SLaurent Vivier qemu_set_info_str(&s->data.nc, "connecting to passt"); 438da703b06SLaurent Vivier 439da703b06SLaurent Vivier /* create chardev */ 440da703b06SLaurent Vivier 441da703b06SLaurent Vivier chr = CHARDEV(object_new(TYPE_CHARDEV_SOCKET)); 442da703b06SLaurent Vivier if (!chr || qemu_chr_add_client(chr, sv[0]) == -1) { 443da703b06SLaurent Vivier object_unref(OBJECT(chr)); 444da703b06SLaurent Vivier error_setg(errp, "Failed to make socket chardev"); 445da703b06SLaurent Vivier goto err; 446da703b06SLaurent Vivier } 447da703b06SLaurent Vivier 448da703b06SLaurent Vivier s->vhost_user = g_new0(struct VhostUserState, 1); 449da703b06SLaurent Vivier if (!qemu_chr_fe_init(&s->vhost_chr, chr, errp) || 450da703b06SLaurent Vivier !vhost_user_init(s->vhost_user, &s->vhost_chr, errp)) { 451da703b06SLaurent Vivier goto err; 452da703b06SLaurent Vivier } 453da703b06SLaurent Vivier 454da703b06SLaurent Vivier /* start passt */ 455da703b06SLaurent Vivier if (net_passt_start_daemon(s, sv[1], errp) == -1) { 456da703b06SLaurent Vivier goto err; 457da703b06SLaurent Vivier } 458da703b06SLaurent Vivier 459da703b06SLaurent Vivier do { 460da703b06SLaurent Vivier if (qemu_chr_fe_wait_connected(&s->vhost_chr, errp) < 0) { 461da703b06SLaurent Vivier goto err; 462da703b06SLaurent Vivier } 463da703b06SLaurent Vivier 464da703b06SLaurent Vivier qemu_chr_fe_set_handlers(&s->vhost_chr, NULL, NULL, 465da703b06SLaurent Vivier passt_vhost_user_event, NULL, s, NULL, 466da703b06SLaurent Vivier true); 467da703b06SLaurent Vivier } while (!s->started); 468da703b06SLaurent Vivier 469da703b06SLaurent Vivier qemu_set_info_str(&s->data.nc, "vhost-user,connected to pid %d", s->pid); 470da703b06SLaurent Vivier 471da703b06SLaurent Vivier close(sv[1]); 472da703b06SLaurent Vivier return 0; 473da703b06SLaurent Vivier err: 474da703b06SLaurent Vivier close(sv[0]); 475da703b06SLaurent Vivier close(sv[1]); 476da703b06SLaurent Vivier 477da703b06SLaurent Vivier return -1; 478da703b06SLaurent Vivier } 479da703b06SLaurent Vivier #else 480da703b06SLaurent Vivier static int net_passt_vhost_user_init(NetPasstState *s, Error **errp) 481da703b06SLaurent Vivier { 482da703b06SLaurent Vivier error_setg(errp, "vhost-user support has not been built"); 483da703b06SLaurent Vivier 484da703b06SLaurent Vivier return -1; 485da703b06SLaurent Vivier } 486da703b06SLaurent Vivier #endif 487da703b06SLaurent Vivier 488854ee02bSLaurent Vivier static GPtrArray *net_passt_decode_args(const NetDevPasstOptions *passt, 489854ee02bSLaurent Vivier gchar *pidfile, Error **errp) 490854ee02bSLaurent Vivier { 491854ee02bSLaurent Vivier GPtrArray *args = g_ptr_array_new_with_free_func(g_free); 492854ee02bSLaurent Vivier 493854ee02bSLaurent Vivier if (passt->path) { 494854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup(passt->path)); 495854ee02bSLaurent Vivier } else { 496854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup("passt")); 497854ee02bSLaurent Vivier } 498854ee02bSLaurent Vivier 499da703b06SLaurent Vivier if (passt->has_vhost_user && passt->vhost_user) { 500da703b06SLaurent Vivier g_ptr_array_add(args, g_strdup("--vhost-user")); 501da703b06SLaurent Vivier } 502da703b06SLaurent Vivier 503854ee02bSLaurent Vivier /* by default, be quiet */ 504854ee02bSLaurent Vivier if (!passt->has_quiet || passt->quiet) { 505854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup("--quiet")); 506854ee02bSLaurent Vivier } 507854ee02bSLaurent Vivier 508854ee02bSLaurent Vivier if (passt->has_mtu) { 509854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup("--mtu")); 510854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup_printf("%"PRId64, passt->mtu)); 511854ee02bSLaurent Vivier } 512854ee02bSLaurent Vivier 513854ee02bSLaurent Vivier if (passt->address) { 514854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup("--address")); 515854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup(passt->address)); 516854ee02bSLaurent Vivier } 517854ee02bSLaurent Vivier 518854ee02bSLaurent Vivier if (passt->netmask) { 519854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup("--netmask")); 520854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup(passt->netmask)); 521854ee02bSLaurent Vivier } 522854ee02bSLaurent Vivier 523854ee02bSLaurent Vivier if (passt->mac) { 524854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup("--mac-addr")); 525854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup(passt->mac)); 526854ee02bSLaurent Vivier } 527854ee02bSLaurent Vivier 528854ee02bSLaurent Vivier if (passt->gateway) { 529854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup("--gateway")); 530854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup(passt->gateway)); 531854ee02bSLaurent Vivier } 532854ee02bSLaurent Vivier 533854ee02bSLaurent Vivier if (passt->interface) { 534854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup("--interface")); 535854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup(passt->interface)); 536854ee02bSLaurent Vivier } 537854ee02bSLaurent Vivier 538854ee02bSLaurent Vivier if (passt->outbound) { 539854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup("--outbound")); 540854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup(passt->outbound)); 541854ee02bSLaurent Vivier } 542854ee02bSLaurent Vivier 543854ee02bSLaurent Vivier if (passt->outbound_if4) { 544854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup("--outbound-if4")); 545854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup(passt->outbound_if4)); 546854ee02bSLaurent Vivier } 547854ee02bSLaurent Vivier 548854ee02bSLaurent Vivier if (passt->outbound_if6) { 549854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup("--outbound-if6")); 550854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup(passt->outbound_if6)); 551854ee02bSLaurent Vivier } 552854ee02bSLaurent Vivier 553854ee02bSLaurent Vivier if (passt->dns) { 554854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup("--dns")); 555854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup(passt->dns)); 556854ee02bSLaurent Vivier } 557854ee02bSLaurent Vivier if (passt->fqdn) { 558854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup("--fqdn")); 559854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup(passt->fqdn)); 560854ee02bSLaurent Vivier } 561854ee02bSLaurent Vivier 562854ee02bSLaurent Vivier if (passt->has_dhcp_dns && !passt->dhcp_dns) { 563854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup("--no-dhcp-dns")); 564854ee02bSLaurent Vivier } 565854ee02bSLaurent Vivier 566854ee02bSLaurent Vivier if (passt->has_dhcp_search && !passt->dhcp_search) { 567854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup("--no-dhcp-search")); 568854ee02bSLaurent Vivier } 569854ee02bSLaurent Vivier 570854ee02bSLaurent Vivier if (passt->map_host_loopback) { 571854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup("--map-host-loopback")); 572854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup(passt->map_host_loopback)); 573854ee02bSLaurent Vivier } 574854ee02bSLaurent Vivier 575854ee02bSLaurent Vivier if (passt->map_guest_addr) { 576854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup("--map-guest-addr")); 577854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup(passt->map_guest_addr)); 578854ee02bSLaurent Vivier } 579854ee02bSLaurent Vivier 580854ee02bSLaurent Vivier if (passt->dns_forward) { 581854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup("--dns-forward")); 582854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup(passt->dns_forward)); 583854ee02bSLaurent Vivier } 584854ee02bSLaurent Vivier 585854ee02bSLaurent Vivier if (passt->dns_host) { 586854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup("--dns-host")); 587854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup(passt->dns_host)); 588854ee02bSLaurent Vivier } 589854ee02bSLaurent Vivier 590854ee02bSLaurent Vivier if (passt->has_tcp && !passt->tcp) { 591854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup("--no-tcp")); 592854ee02bSLaurent Vivier } 593854ee02bSLaurent Vivier 594854ee02bSLaurent Vivier if (passt->has_udp && !passt->udp) { 595854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup("--no-udp")); 596854ee02bSLaurent Vivier } 597854ee02bSLaurent Vivier 598854ee02bSLaurent Vivier if (passt->has_icmp && !passt->icmp) { 599854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup("--no-icmp")); 600854ee02bSLaurent Vivier } 601854ee02bSLaurent Vivier 602854ee02bSLaurent Vivier if (passt->has_dhcp && !passt->dhcp) { 603854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup("--no-dhcp")); 604854ee02bSLaurent Vivier } 605854ee02bSLaurent Vivier 606854ee02bSLaurent Vivier if (passt->has_ndp && !passt->ndp) { 607854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup("--no-ndp")); 608854ee02bSLaurent Vivier } 609854ee02bSLaurent Vivier if (passt->has_dhcpv6 && !passt->dhcpv6) { 610854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup("--no-dhcpv6")); 611854ee02bSLaurent Vivier } 612854ee02bSLaurent Vivier 613854ee02bSLaurent Vivier if (passt->has_ra && !passt->ra) { 614854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup("--no-ra")); 615854ee02bSLaurent Vivier } 616854ee02bSLaurent Vivier 617854ee02bSLaurent Vivier if (passt->has_freebind && passt->freebind) { 618854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup("--freebind")); 619854ee02bSLaurent Vivier } 620854ee02bSLaurent Vivier 621854ee02bSLaurent Vivier if (passt->has_ipv4 && !passt->ipv4) { 622854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup("--ipv6-only")); 623854ee02bSLaurent Vivier } 624854ee02bSLaurent Vivier 625854ee02bSLaurent Vivier if (passt->has_ipv6 && !passt->ipv6) { 626854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup("--ipv4-only")); 627854ee02bSLaurent Vivier } 628854ee02bSLaurent Vivier 629854ee02bSLaurent Vivier if (passt->has_search && passt->search) { 630854ee02bSLaurent Vivier const StringList *list = passt->search; 631854ee02bSLaurent Vivier GString *domains = g_string_new(list->value->str); 632854ee02bSLaurent Vivier 633854ee02bSLaurent Vivier list = list->next; 634854ee02bSLaurent Vivier while (list) { 635854ee02bSLaurent Vivier g_string_append(domains, " "); 636854ee02bSLaurent Vivier g_string_append(domains, list->value->str); 637854ee02bSLaurent Vivier list = list->next; 638854ee02bSLaurent Vivier } 639854ee02bSLaurent Vivier 640854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup("--search")); 641854ee02bSLaurent Vivier g_ptr_array_add(args, g_string_free(domains, FALSE)); 642854ee02bSLaurent Vivier } 643854ee02bSLaurent Vivier 644854ee02bSLaurent Vivier if (passt->has_tcp_ports && passt->tcp_ports) { 645854ee02bSLaurent Vivier const StringList *list = passt->tcp_ports; 646854ee02bSLaurent Vivier GString *tcp_ports = g_string_new(list->value->str); 647854ee02bSLaurent Vivier 648854ee02bSLaurent Vivier list = list->next; 649854ee02bSLaurent Vivier while (list) { 650854ee02bSLaurent Vivier g_string_append(tcp_ports, ","); 651854ee02bSLaurent Vivier g_string_append(tcp_ports, list->value->str); 652854ee02bSLaurent Vivier list = list->next; 653854ee02bSLaurent Vivier } 654854ee02bSLaurent Vivier 655854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup("--tcp-ports")); 656854ee02bSLaurent Vivier g_ptr_array_add(args, g_string_free(tcp_ports, FALSE)); 657854ee02bSLaurent Vivier } 658854ee02bSLaurent Vivier 659854ee02bSLaurent Vivier if (passt->has_udp_ports && passt->udp_ports) { 660854ee02bSLaurent Vivier const StringList *list = passt->udp_ports; 661854ee02bSLaurent Vivier GString *udp_ports = g_string_new(list->value->str); 662854ee02bSLaurent Vivier 663854ee02bSLaurent Vivier list = list->next; 664854ee02bSLaurent Vivier while (list) { 665854ee02bSLaurent Vivier g_string_append(udp_ports, ","); 666854ee02bSLaurent Vivier g_string_append(udp_ports, list->value->str); 667854ee02bSLaurent Vivier list = list->next; 668854ee02bSLaurent Vivier } 669854ee02bSLaurent Vivier 670854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup("--udp-ports")); 671854ee02bSLaurent Vivier g_ptr_array_add(args, g_string_free(udp_ports, FALSE)); 672854ee02bSLaurent Vivier } 673854ee02bSLaurent Vivier 674854ee02bSLaurent Vivier if (passt->has_param && passt->param) { 675854ee02bSLaurent Vivier const StringList *list = passt->param; 676854ee02bSLaurent Vivier 677854ee02bSLaurent Vivier while (list) { 678854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup(list->value->str)); 679854ee02bSLaurent Vivier list = list->next; 680854ee02bSLaurent Vivier } 681854ee02bSLaurent Vivier } 682854ee02bSLaurent Vivier 683854ee02bSLaurent Vivier /* provide a pid file to be able to kil passt on exit */ 684854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup("--pid")); 685854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup(pidfile)); 686854ee02bSLaurent Vivier 687854ee02bSLaurent Vivier /* g_subprocess_launcher_take_fd() will set the socket on fd 3 */ 688854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup("--fd")); 689854ee02bSLaurent Vivier g_ptr_array_add(args, g_strdup("3")); 690854ee02bSLaurent Vivier 691854ee02bSLaurent Vivier g_ptr_array_add(args, NULL); 692854ee02bSLaurent Vivier 693854ee02bSLaurent Vivier return args; 694854ee02bSLaurent Vivier } 695854ee02bSLaurent Vivier 696854ee02bSLaurent Vivier int net_init_passt(const Netdev *netdev, const char *name, 697854ee02bSLaurent Vivier NetClientState *peer, Error **errp) 698854ee02bSLaurent Vivier { 699854ee02bSLaurent Vivier g_autoptr(GError) error = NULL; 700854ee02bSLaurent Vivier NetClientState *nc; 701854ee02bSLaurent Vivier NetPasstState *s; 702854ee02bSLaurent Vivier GPtrArray *args; 703854ee02bSLaurent Vivier gchar *pidfile; 704854ee02bSLaurent Vivier int pidfd; 705854ee02bSLaurent Vivier 706854ee02bSLaurent Vivier assert(netdev->type == NET_CLIENT_DRIVER_PASST); 707854ee02bSLaurent Vivier 708854ee02bSLaurent Vivier pidfd = g_file_open_tmp("passt-XXXXXX.pid", &pidfile, &error); 709854ee02bSLaurent Vivier if (pidfd == -1) { 710854ee02bSLaurent Vivier error_setg(errp, "Failed to create temporary file: %s", error->message); 711854ee02bSLaurent Vivier return -1; 712854ee02bSLaurent Vivier } 713854ee02bSLaurent Vivier close(pidfd); 714854ee02bSLaurent Vivier 715854ee02bSLaurent Vivier args = net_passt_decode_args(&netdev->u.passt, pidfile, errp); 716854ee02bSLaurent Vivier if (args == NULL) { 717854ee02bSLaurent Vivier g_free(pidfile); 718854ee02bSLaurent Vivier return -1; 719854ee02bSLaurent Vivier } 720854ee02bSLaurent Vivier 721854ee02bSLaurent Vivier nc = qemu_new_net_client(&net_passt_info, peer, "passt", name); 722854ee02bSLaurent Vivier s = DO_UPCAST(NetPasstState, data.nc, nc); 723854ee02bSLaurent Vivier 724854ee02bSLaurent Vivier s->args = args; 725854ee02bSLaurent Vivier s->pidfile = pidfile; 726854ee02bSLaurent Vivier 727da703b06SLaurent Vivier if (netdev->u.passt.has_vhost_user && netdev->u.passt.vhost_user) { 728da703b06SLaurent Vivier if (net_passt_vhost_user_init(s, errp) == -1) { 729da703b06SLaurent Vivier qemu_del_net_client(nc); 730da703b06SLaurent Vivier return -1; 731da703b06SLaurent Vivier } 732da703b06SLaurent Vivier 733da703b06SLaurent Vivier return 0; 734da703b06SLaurent Vivier } 735da703b06SLaurent Vivier 736854ee02bSLaurent Vivier if (net_passt_stream_start(s, errp) == -1) { 737854ee02bSLaurent Vivier qemu_del_net_client(nc); 738854ee02bSLaurent Vivier return -1; 739854ee02bSLaurent Vivier } 740854ee02bSLaurent Vivier 741854ee02bSLaurent Vivier return 0; 742854ee02bSLaurent Vivier } 743