1 /* 2 * passt network backend 3 * 4 * Copyright Red Hat 5 * 6 * SPDX-License-Identifier: GPL-2.0-or-later 7 */ 8 #include "qemu/osdep.h" 9 #include <glib/gstdio.h> 10 #include "qemu/error-report.h" 11 #include <gio/gio.h> 12 #include "net/net.h" 13 #include "clients.h" 14 #include "qapi/error.h" 15 #include "io/net-listener.h" 16 #include "chardev/char-fe.h" 17 #include "net/vhost_net.h" 18 #include "hw/virtio/vhost.h" 19 #include "hw/virtio/vhost-user.h" 20 #include "standard-headers/linux/virtio_net.h" 21 #include "stream_data.h" 22 23 #ifdef CONFIG_VHOST_USER 24 static const int user_feature_bits[] = { 25 VIRTIO_F_NOTIFY_ON_EMPTY, 26 VIRTIO_F_NOTIFICATION_DATA, 27 VIRTIO_RING_F_INDIRECT_DESC, 28 VIRTIO_RING_F_EVENT_IDX, 29 30 VIRTIO_F_ANY_LAYOUT, 31 VIRTIO_F_VERSION_1, 32 VIRTIO_NET_F_CSUM, 33 VIRTIO_NET_F_GUEST_CSUM, 34 VIRTIO_NET_F_GSO, 35 VIRTIO_NET_F_GUEST_TSO4, 36 VIRTIO_NET_F_GUEST_TSO6, 37 VIRTIO_NET_F_GUEST_ECN, 38 VIRTIO_NET_F_GUEST_UFO, 39 VIRTIO_NET_F_HOST_TSO4, 40 VIRTIO_NET_F_HOST_TSO6, 41 VIRTIO_NET_F_HOST_ECN, 42 VIRTIO_NET_F_HOST_UFO, 43 VIRTIO_NET_F_MRG_RXBUF, 44 VIRTIO_NET_F_MTU, 45 VIRTIO_F_IOMMU_PLATFORM, 46 VIRTIO_F_RING_PACKED, 47 VIRTIO_F_RING_RESET, 48 VIRTIO_F_IN_ORDER, 49 VIRTIO_NET_F_RSS, 50 VIRTIO_NET_F_RSC_EXT, 51 VIRTIO_NET_F_HASH_REPORT, 52 VIRTIO_NET_F_GUEST_USO4, 53 VIRTIO_NET_F_GUEST_USO6, 54 VIRTIO_NET_F_HOST_USO, 55 56 /* This bit implies RARP isn't sent by QEMU out of band */ 57 VIRTIO_NET_F_GUEST_ANNOUNCE, 58 59 VIRTIO_NET_F_MQ, 60 61 VHOST_INVALID_FEATURE_BIT 62 }; 63 #endif 64 65 typedef struct NetPasstState { 66 NetStreamData data; 67 GPtrArray *args; 68 gchar *pidfile; 69 pid_t pid; 70 #ifdef CONFIG_VHOST_USER 71 /* vhost user */ 72 VhostUserState *vhost_user; 73 VHostNetState *vhost_net; 74 CharBackend vhost_chr; 75 guint vhost_watch; 76 uint64_t acked_features; 77 bool started; 78 #endif 79 } NetPasstState; 80 81 static int net_passt_stream_start(NetPasstState *s, Error **errp); 82 83 static void net_passt_cleanup(NetClientState *nc) 84 { 85 NetPasstState *s = DO_UPCAST(NetPasstState, data.nc, nc); 86 87 #ifdef CONFIG_VHOST_USER 88 if (s->vhost_net) { 89 vhost_net_cleanup(s->vhost_net); 90 g_free(s->vhost_net); 91 s->vhost_net = NULL; 92 } 93 if (s->vhost_watch) { 94 g_source_remove(s->vhost_watch); 95 s->vhost_watch = 0; 96 } 97 qemu_chr_fe_deinit(&s->vhost_chr, true); 98 if (s->vhost_user) { 99 vhost_user_cleanup(s->vhost_user); 100 g_free(s->vhost_user); 101 s->vhost_user = NULL; 102 } 103 #endif 104 105 kill(s->pid, SIGTERM); 106 g_remove(s->pidfile); 107 g_free(s->pidfile); 108 g_ptr_array_free(s->args, TRUE); 109 } 110 111 static ssize_t net_passt_receive(NetClientState *nc, const uint8_t *buf, 112 size_t size) 113 { 114 NetStreamData *d = DO_UPCAST(NetStreamData, nc, nc); 115 116 return net_stream_data_receive(d, buf, size); 117 } 118 119 static gboolean net_passt_send(QIOChannel *ioc, GIOCondition condition, 120 gpointer data) 121 { 122 if (net_stream_data_send(ioc, condition, data) == G_SOURCE_REMOVE) { 123 NetPasstState *s = DO_UPCAST(NetPasstState, data, data); 124 Error *error; 125 126 /* we need to restart passt */ 127 kill(s->pid, SIGTERM); 128 if (net_passt_stream_start(s, &error) == -1) { 129 error_report_err(error); 130 } 131 132 return G_SOURCE_REMOVE; 133 } 134 135 return G_SOURCE_CONTINUE; 136 } 137 138 #ifdef CONFIG_VHOST_USER 139 static int passt_set_vnet_endianness(NetClientState *nc, bool enable) 140 { 141 assert(nc->info->type == NET_CLIENT_DRIVER_PASST); 142 143 return 0; 144 } 145 146 static bool passt_has_vnet_hdr(NetClientState *nc) 147 { 148 NetPasstState *s = DO_UPCAST(NetPasstState, data.nc, nc); 149 150 assert(nc->info->type == NET_CLIENT_DRIVER_PASST); 151 152 return s->vhost_user != NULL; 153 } 154 155 static bool passt_has_ufo(NetClientState *nc) 156 { 157 NetPasstState *s = DO_UPCAST(NetPasstState, data.nc, nc); 158 159 assert(nc->info->type == NET_CLIENT_DRIVER_PASST); 160 161 return s->vhost_user != NULL; 162 } 163 164 static bool passt_check_peer_type(NetClientState *nc, ObjectClass *oc, 165 Error **errp) 166 { 167 NetPasstState *s = DO_UPCAST(NetPasstState, data.nc, nc); 168 const char *driver = object_class_get_name(oc); 169 170 assert(nc->info->type == NET_CLIENT_DRIVER_PASST); 171 172 if (s->vhost_user == NULL) { 173 return true; 174 } 175 176 if (!g_str_has_prefix(driver, "virtio-net-")) { 177 error_setg(errp, "vhost-user requires frontend driver virtio-net-*"); 178 return false; 179 } 180 181 return true; 182 } 183 184 static struct vhost_net *passt_get_vhost_net(NetClientState *nc) 185 { 186 NetPasstState *s = DO_UPCAST(NetPasstState, data.nc, nc); 187 188 assert(nc->info->type == NET_CLIENT_DRIVER_PASST); 189 190 return s->vhost_net; 191 } 192 193 static uint64_t passt_get_acked_features(NetClientState *nc) 194 { 195 NetPasstState *s = DO_UPCAST(NetPasstState, data.nc, nc); 196 197 assert(nc->info->type == NET_CLIENT_DRIVER_PASST); 198 199 return s->acked_features; 200 } 201 202 static void passt_save_acked_features(NetClientState *nc) 203 { 204 NetPasstState *s = DO_UPCAST(NetPasstState, data.nc, nc); 205 206 assert(nc->info->type == NET_CLIENT_DRIVER_PASST); 207 208 if (s->vhost_net) { 209 uint64_t features = vhost_net_get_acked_features(s->vhost_net); 210 if (features) { 211 s->acked_features = features; 212 } 213 } 214 } 215 #endif 216 217 static NetClientInfo net_passt_info = { 218 .type = NET_CLIENT_DRIVER_PASST, 219 .size = sizeof(NetPasstState), 220 .receive = net_passt_receive, 221 .cleanup = net_passt_cleanup, 222 #ifdef CONFIG_VHOST_USER 223 .has_vnet_hdr = passt_has_vnet_hdr, 224 .has_ufo = passt_has_ufo, 225 .set_vnet_be = passt_set_vnet_endianness, 226 .set_vnet_le = passt_set_vnet_endianness, 227 .check_peer_type = passt_check_peer_type, 228 .get_vhost_net = passt_get_vhost_net, 229 #endif 230 }; 231 232 static void net_passt_client_connected(QIOTask *task, gpointer opaque) 233 { 234 NetPasstState *s = opaque; 235 236 if (net_stream_data_client_connected(task, &s->data) == 0) { 237 qemu_set_info_str(&s->data.nc, "stream,connected to pid %d", s->pid); 238 } 239 } 240 241 static int net_passt_start_daemon(NetPasstState *s, int sock, Error **errp) 242 { 243 g_autoptr(GSubprocess) daemon = NULL; 244 g_autofree gchar *contents = NULL; 245 g_autoptr(GError) error = NULL; 246 GSubprocessLauncher *launcher; 247 248 qemu_set_info_str(&s->data.nc, "launching passt"); 249 250 launcher = g_subprocess_launcher_new(G_SUBPROCESS_FLAGS_NONE); 251 g_subprocess_launcher_take_fd(launcher, sock, 3); 252 253 daemon = g_subprocess_launcher_spawnv(launcher, 254 (const gchar *const *)s->args->pdata, 255 &error); 256 g_object_unref(launcher); 257 258 if (!daemon) { 259 error_setg(errp, "Error creating daemon: %s", error->message); 260 return -1; 261 } 262 263 if (!g_subprocess_wait(daemon, NULL, &error)) { 264 error_setg(errp, "Error waiting for daemon: %s", error->message); 265 return -1; 266 } 267 268 if (g_subprocess_get_if_exited(daemon) && 269 g_subprocess_get_exit_status(daemon)) { 270 return -1; 271 } 272 273 if (!g_file_get_contents(s->pidfile, &contents, NULL, &error)) { 274 error_setg(errp, "Cannot read passt pid: %s", error->message); 275 return -1; 276 } 277 278 s->pid = (pid_t)g_ascii_strtoll(contents, NULL, 10); 279 if (s->pid <= 0) { 280 error_setg(errp, "File '%s' did not contain a valid PID.", s->pidfile); 281 return -1; 282 } 283 284 return 0; 285 } 286 287 static int net_passt_stream_start(NetPasstState *s, Error **errp) 288 { 289 QIOChannelSocket *sioc; 290 SocketAddress *addr; 291 int sv[2]; 292 293 if (socketpair(PF_UNIX, SOCK_STREAM, 0, sv) == -1) { 294 error_setg_errno(errp, errno, "socketpair() failed"); 295 return -1; 296 } 297 298 /* connect to passt */ 299 qemu_set_info_str(&s->data.nc, "connecting to passt"); 300 301 /* create socket channel */ 302 sioc = qio_channel_socket_new(); 303 s->data.ioc = QIO_CHANNEL(sioc); 304 s->data.nc.link_down = true; 305 s->data.send = net_passt_send; 306 307 addr = g_new0(SocketAddress, 1); 308 addr->type = SOCKET_ADDRESS_TYPE_FD; 309 addr->u.fd.str = g_strdup_printf("%d", sv[0]); 310 311 qio_channel_socket_connect_async(sioc, addr, 312 net_passt_client_connected, s, 313 NULL, NULL); 314 315 qapi_free_SocketAddress(addr); 316 317 /* start passt */ 318 if (net_passt_start_daemon(s, sv[1], errp) == -1) { 319 close(sv[0]); 320 close(sv[1]); 321 return -1; 322 } 323 close(sv[1]); 324 325 return 0; 326 } 327 328 #ifdef CONFIG_VHOST_USER 329 static gboolean passt_vhost_user_watch(void *do_not_use, GIOCondition cond, 330 void *opaque) 331 { 332 NetPasstState *s = opaque; 333 334 qemu_chr_fe_disconnect(&s->vhost_chr); 335 336 return G_SOURCE_CONTINUE; 337 } 338 339 static void passt_vhost_user_event(void *opaque, QEMUChrEvent event); 340 341 static void chr_closed_bh(void *opaque) 342 { 343 NetPasstState *s = opaque; 344 345 passt_save_acked_features(&s->data.nc); 346 347 net_client_set_link(&(NetClientState *){ &s->data.nc }, 1, false); 348 349 qemu_chr_fe_set_handlers(&s->vhost_chr, NULL, NULL, passt_vhost_user_event, 350 NULL, s, NULL, true); 351 } 352 353 static void passt_vhost_user_stop(NetPasstState *s) 354 { 355 passt_save_acked_features(&s->data.nc); 356 vhost_net_cleanup(s->vhost_net); 357 } 358 359 static int passt_vhost_user_start(NetPasstState *s, VhostUserState *be) 360 { 361 struct vhost_net *net = NULL; 362 VhostNetOptions options; 363 364 options.backend_type = VHOST_BACKEND_TYPE_USER; 365 options.net_backend = &s->data.nc; 366 options.opaque = be; 367 options.busyloop_timeout = 0; 368 options.nvqs = 2; 369 options.feature_bits = user_feature_bits; 370 options.max_tx_queue_size = VIRTQUEUE_MAX_SIZE; 371 options.get_acked_features = passt_get_acked_features; 372 options.save_acked_features = passt_save_acked_features; 373 options.is_vhost_user = true; 374 375 net = vhost_net_init(&options); 376 if (!net) { 377 error_report("failed to init passt vhost_net"); 378 goto err; 379 } 380 381 if (s->vhost_net) { 382 vhost_net_cleanup(s->vhost_net); 383 g_free(s->vhost_net); 384 } 385 s->vhost_net = net; 386 387 return 0; 388 err: 389 if (net) { 390 vhost_net_cleanup(net); 391 g_free(net); 392 } 393 passt_vhost_user_stop(s); 394 return -1; 395 } 396 397 static void passt_vhost_user_event(void *opaque, QEMUChrEvent event) 398 { 399 NetPasstState *s = opaque; 400 401 switch (event) { 402 case CHR_EVENT_OPENED: 403 if (passt_vhost_user_start(s, s->vhost_user) < 0) { 404 qemu_chr_fe_disconnect(&s->vhost_chr); 405 return; 406 } 407 s->vhost_watch = qemu_chr_fe_add_watch(&s->vhost_chr, G_IO_HUP, 408 passt_vhost_user_watch, s); 409 net_client_set_link(&(NetClientState *){ &s->data.nc }, 1, true); 410 s->started = true; 411 break; 412 case CHR_EVENT_CLOSED: 413 if (s->vhost_watch) { 414 AioContext *ctx = qemu_get_current_aio_context(); 415 416 g_source_remove(s->vhost_watch); 417 s->vhost_watch = 0; 418 qemu_chr_fe_set_handlers(&s->vhost_chr, NULL, NULL, NULL, NULL, 419 NULL, NULL, false); 420 421 aio_bh_schedule_oneshot(ctx, chr_closed_bh, s); 422 } 423 break; 424 case CHR_EVENT_BREAK: 425 case CHR_EVENT_MUX_IN: 426 case CHR_EVENT_MUX_OUT: 427 /* Ignore */ 428 break; 429 } 430 } 431 432 static int net_passt_vhost_user_init(NetPasstState *s, Error **errp) 433 { 434 Chardev *chr; 435 int sv[2]; 436 437 if (socketpair(PF_UNIX, SOCK_STREAM, 0, sv) == -1) { 438 error_setg_errno(errp, errno, "socketpair() failed"); 439 return -1; 440 } 441 442 /* connect to passt */ 443 qemu_set_info_str(&s->data.nc, "connecting to passt"); 444 445 /* create chardev */ 446 447 chr = CHARDEV(object_new(TYPE_CHARDEV_SOCKET)); 448 if (!chr || qemu_chr_add_client(chr, sv[0]) == -1) { 449 object_unref(OBJECT(chr)); 450 error_setg(errp, "Failed to make socket chardev"); 451 goto err; 452 } 453 454 s->vhost_user = g_new0(struct VhostUserState, 1); 455 if (!qemu_chr_fe_init(&s->vhost_chr, chr, errp) || 456 !vhost_user_init(s->vhost_user, &s->vhost_chr, errp)) { 457 goto err; 458 } 459 460 /* start passt */ 461 if (net_passt_start_daemon(s, sv[1], errp) == -1) { 462 goto err; 463 } 464 465 do { 466 if (qemu_chr_fe_wait_connected(&s->vhost_chr, errp) < 0) { 467 goto err; 468 } 469 470 qemu_chr_fe_set_handlers(&s->vhost_chr, NULL, NULL, 471 passt_vhost_user_event, NULL, s, NULL, 472 true); 473 } while (!s->started); 474 475 qemu_set_info_str(&s->data.nc, "vhost-user,connected to pid %d", s->pid); 476 477 close(sv[1]); 478 return 0; 479 err: 480 close(sv[0]); 481 close(sv[1]); 482 483 return -1; 484 } 485 #else 486 static int net_passt_vhost_user_init(NetPasstState *s, Error **errp) 487 { 488 error_setg(errp, "vhost-user support has not been built"); 489 490 return -1; 491 } 492 #endif 493 494 static GPtrArray *net_passt_decode_args(const NetDevPasstOptions *passt, 495 gchar *pidfile, Error **errp) 496 { 497 GPtrArray *args = g_ptr_array_new_with_free_func(g_free); 498 499 if (passt->path) { 500 g_ptr_array_add(args, g_strdup(passt->path)); 501 } else { 502 g_ptr_array_add(args, g_strdup("passt")); 503 } 504 505 if (passt->has_vhost_user && passt->vhost_user) { 506 g_ptr_array_add(args, g_strdup("--vhost-user")); 507 } 508 509 /* by default, be quiet */ 510 if (!passt->has_quiet || passt->quiet) { 511 g_ptr_array_add(args, g_strdup("--quiet")); 512 } 513 514 if (passt->has_mtu) { 515 g_ptr_array_add(args, g_strdup("--mtu")); 516 g_ptr_array_add(args, g_strdup_printf("%"PRId64, passt->mtu)); 517 } 518 519 if (passt->address) { 520 g_ptr_array_add(args, g_strdup("--address")); 521 g_ptr_array_add(args, g_strdup(passt->address)); 522 } 523 524 if (passt->netmask) { 525 g_ptr_array_add(args, g_strdup("--netmask")); 526 g_ptr_array_add(args, g_strdup(passt->netmask)); 527 } 528 529 if (passt->mac) { 530 g_ptr_array_add(args, g_strdup("--mac-addr")); 531 g_ptr_array_add(args, g_strdup(passt->mac)); 532 } 533 534 if (passt->gateway) { 535 g_ptr_array_add(args, g_strdup("--gateway")); 536 g_ptr_array_add(args, g_strdup(passt->gateway)); 537 } 538 539 if (passt->interface) { 540 g_ptr_array_add(args, g_strdup("--interface")); 541 g_ptr_array_add(args, g_strdup(passt->interface)); 542 } 543 544 if (passt->outbound) { 545 g_ptr_array_add(args, g_strdup("--outbound")); 546 g_ptr_array_add(args, g_strdup(passt->outbound)); 547 } 548 549 if (passt->outbound_if4) { 550 g_ptr_array_add(args, g_strdup("--outbound-if4")); 551 g_ptr_array_add(args, g_strdup(passt->outbound_if4)); 552 } 553 554 if (passt->outbound_if6) { 555 g_ptr_array_add(args, g_strdup("--outbound-if6")); 556 g_ptr_array_add(args, g_strdup(passt->outbound_if6)); 557 } 558 559 if (passt->dns) { 560 g_ptr_array_add(args, g_strdup("--dns")); 561 g_ptr_array_add(args, g_strdup(passt->dns)); 562 } 563 if (passt->fqdn) { 564 g_ptr_array_add(args, g_strdup("--fqdn")); 565 g_ptr_array_add(args, g_strdup(passt->fqdn)); 566 } 567 568 if (passt->has_dhcp_dns && !passt->dhcp_dns) { 569 g_ptr_array_add(args, g_strdup("--no-dhcp-dns")); 570 } 571 572 if (passt->has_dhcp_search && !passt->dhcp_search) { 573 g_ptr_array_add(args, g_strdup("--no-dhcp-search")); 574 } 575 576 if (passt->map_host_loopback) { 577 g_ptr_array_add(args, g_strdup("--map-host-loopback")); 578 g_ptr_array_add(args, g_strdup(passt->map_host_loopback)); 579 } 580 581 if (passt->map_guest_addr) { 582 g_ptr_array_add(args, g_strdup("--map-guest-addr")); 583 g_ptr_array_add(args, g_strdup(passt->map_guest_addr)); 584 } 585 586 if (passt->dns_forward) { 587 g_ptr_array_add(args, g_strdup("--dns-forward")); 588 g_ptr_array_add(args, g_strdup(passt->dns_forward)); 589 } 590 591 if (passt->dns_host) { 592 g_ptr_array_add(args, g_strdup("--dns-host")); 593 g_ptr_array_add(args, g_strdup(passt->dns_host)); 594 } 595 596 if (passt->has_tcp && !passt->tcp) { 597 g_ptr_array_add(args, g_strdup("--no-tcp")); 598 } 599 600 if (passt->has_udp && !passt->udp) { 601 g_ptr_array_add(args, g_strdup("--no-udp")); 602 } 603 604 if (passt->has_icmp && !passt->icmp) { 605 g_ptr_array_add(args, g_strdup("--no-icmp")); 606 } 607 608 if (passt->has_dhcp && !passt->dhcp) { 609 g_ptr_array_add(args, g_strdup("--no-dhcp")); 610 } 611 612 if (passt->has_ndp && !passt->ndp) { 613 g_ptr_array_add(args, g_strdup("--no-ndp")); 614 } 615 if (passt->has_dhcpv6 && !passt->dhcpv6) { 616 g_ptr_array_add(args, g_strdup("--no-dhcpv6")); 617 } 618 619 if (passt->has_ra && !passt->ra) { 620 g_ptr_array_add(args, g_strdup("--no-ra")); 621 } 622 623 if (passt->has_freebind && passt->freebind) { 624 g_ptr_array_add(args, g_strdup("--freebind")); 625 } 626 627 if (passt->has_ipv4 && !passt->ipv4) { 628 g_ptr_array_add(args, g_strdup("--ipv6-only")); 629 } 630 631 if (passt->has_ipv6 && !passt->ipv6) { 632 g_ptr_array_add(args, g_strdup("--ipv4-only")); 633 } 634 635 if (passt->has_search && passt->search) { 636 const StringList *list = passt->search; 637 GString *domains = g_string_new(list->value->str); 638 639 list = list->next; 640 while (list) { 641 g_string_append(domains, " "); 642 g_string_append(domains, list->value->str); 643 list = list->next; 644 } 645 646 g_ptr_array_add(args, g_strdup("--search")); 647 g_ptr_array_add(args, g_string_free(domains, FALSE)); 648 } 649 650 if (passt->has_tcp_ports && passt->tcp_ports) { 651 const StringList *list = passt->tcp_ports; 652 GString *tcp_ports = g_string_new(list->value->str); 653 654 list = list->next; 655 while (list) { 656 g_string_append(tcp_ports, ","); 657 g_string_append(tcp_ports, list->value->str); 658 list = list->next; 659 } 660 661 g_ptr_array_add(args, g_strdup("--tcp-ports")); 662 g_ptr_array_add(args, g_string_free(tcp_ports, FALSE)); 663 } 664 665 if (passt->has_udp_ports && passt->udp_ports) { 666 const StringList *list = passt->udp_ports; 667 GString *udp_ports = g_string_new(list->value->str); 668 669 list = list->next; 670 while (list) { 671 g_string_append(udp_ports, ","); 672 g_string_append(udp_ports, list->value->str); 673 list = list->next; 674 } 675 676 g_ptr_array_add(args, g_strdup("--udp-ports")); 677 g_ptr_array_add(args, g_string_free(udp_ports, FALSE)); 678 } 679 680 if (passt->has_param && passt->param) { 681 const StringList *list = passt->param; 682 683 while (list) { 684 g_ptr_array_add(args, g_strdup(list->value->str)); 685 list = list->next; 686 } 687 } 688 689 /* provide a pid file to be able to kil passt on exit */ 690 g_ptr_array_add(args, g_strdup("--pid")); 691 g_ptr_array_add(args, g_strdup(pidfile)); 692 693 /* g_subprocess_launcher_take_fd() will set the socket on fd 3 */ 694 g_ptr_array_add(args, g_strdup("--fd")); 695 g_ptr_array_add(args, g_strdup("3")); 696 697 g_ptr_array_add(args, NULL); 698 699 return args; 700 } 701 702 int net_init_passt(const Netdev *netdev, const char *name, 703 NetClientState *peer, Error **errp) 704 { 705 g_autoptr(GError) error = NULL; 706 NetClientState *nc; 707 NetPasstState *s; 708 GPtrArray *args; 709 gchar *pidfile; 710 int pidfd; 711 712 assert(netdev->type == NET_CLIENT_DRIVER_PASST); 713 714 pidfd = g_file_open_tmp("passt-XXXXXX.pid", &pidfile, &error); 715 if (pidfd == -1) { 716 error_setg(errp, "Failed to create temporary file: %s", error->message); 717 return -1; 718 } 719 close(pidfd); 720 721 args = net_passt_decode_args(&netdev->u.passt, pidfile, errp); 722 if (args == NULL) { 723 g_free(pidfile); 724 return -1; 725 } 726 727 nc = qemu_new_net_client(&net_passt_info, peer, "passt", name); 728 s = DO_UPCAST(NetPasstState, data.nc, nc); 729 730 s->args = args; 731 s->pidfile = pidfile; 732 733 if (netdev->u.passt.has_vhost_user && netdev->u.passt.vhost_user) { 734 if (net_passt_vhost_user_init(s, errp) == -1) { 735 qemu_del_net_client(nc); 736 return -1; 737 } 738 739 return 0; 740 } 741 742 if (net_passt_stream_start(s, errp) == -1) { 743 qemu_del_net_client(nc); 744 return -1; 745 } 746 747 return 0; 748 } 749