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