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 Error *err = NULL; 401 402 switch (event) { 403 case CHR_EVENT_OPENED: 404 if (passt_vhost_user_start(s, s->vhost_user) < 0) { 405 qemu_chr_fe_disconnect(&s->vhost_chr); 406 return; 407 } 408 s->vhost_watch = qemu_chr_fe_add_watch(&s->vhost_chr, G_IO_HUP, 409 passt_vhost_user_watch, s); 410 net_client_set_link(&(NetClientState *){ &s->data.nc }, 1, true); 411 s->started = true; 412 break; 413 case CHR_EVENT_CLOSED: 414 if (s->vhost_watch) { 415 AioContext *ctx = qemu_get_current_aio_context(); 416 417 g_source_remove(s->vhost_watch); 418 s->vhost_watch = 0; 419 qemu_chr_fe_set_handlers(&s->vhost_chr, NULL, NULL, NULL, NULL, 420 NULL, NULL, false); 421 422 aio_bh_schedule_oneshot(ctx, chr_closed_bh, s); 423 } 424 break; 425 case CHR_EVENT_BREAK: 426 case CHR_EVENT_MUX_IN: 427 case CHR_EVENT_MUX_OUT: 428 /* Ignore */ 429 break; 430 } 431 432 if (err) { 433 error_report_err(err); 434 } 435 } 436 437 static int net_passt_vhost_user_init(NetPasstState *s, Error **errp) 438 { 439 Chardev *chr; 440 int sv[2]; 441 442 if (socketpair(PF_UNIX, SOCK_STREAM, 0, sv) == -1) { 443 error_setg_errno(errp, errno, "socketpair() failed"); 444 return -1; 445 } 446 447 /* connect to passt */ 448 qemu_set_info_str(&s->data.nc, "connecting to passt"); 449 450 /* create chardev */ 451 452 chr = CHARDEV(object_new(TYPE_CHARDEV_SOCKET)); 453 if (!chr || qemu_chr_add_client(chr, sv[0]) == -1) { 454 object_unref(OBJECT(chr)); 455 error_setg(errp, "Failed to make socket chardev"); 456 goto err; 457 } 458 459 s->vhost_user = g_new0(struct VhostUserState, 1); 460 if (!qemu_chr_fe_init(&s->vhost_chr, chr, errp) || 461 !vhost_user_init(s->vhost_user, &s->vhost_chr, errp)) { 462 goto err; 463 } 464 465 /* start passt */ 466 if (net_passt_start_daemon(s, sv[1], errp) == -1) { 467 goto err; 468 } 469 470 do { 471 if (qemu_chr_fe_wait_connected(&s->vhost_chr, errp) < 0) { 472 goto err; 473 } 474 475 qemu_chr_fe_set_handlers(&s->vhost_chr, NULL, NULL, 476 passt_vhost_user_event, NULL, s, NULL, 477 true); 478 } while (!s->started); 479 480 qemu_set_info_str(&s->data.nc, "vhost-user,connected to pid %d", s->pid); 481 482 close(sv[1]); 483 return 0; 484 err: 485 close(sv[0]); 486 close(sv[1]); 487 488 return -1; 489 } 490 #else 491 static int net_passt_vhost_user_init(NetPasstState *s, Error **errp) 492 { 493 error_setg(errp, "vhost-user support has not been built"); 494 495 return -1; 496 } 497 #endif 498 499 static GPtrArray *net_passt_decode_args(const NetDevPasstOptions *passt, 500 gchar *pidfile, Error **errp) 501 { 502 GPtrArray *args = g_ptr_array_new_with_free_func(g_free); 503 504 if (passt->path) { 505 g_ptr_array_add(args, g_strdup(passt->path)); 506 } else { 507 g_ptr_array_add(args, g_strdup("passt")); 508 } 509 510 if (passt->has_vhost_user && passt->vhost_user) { 511 g_ptr_array_add(args, g_strdup("--vhost-user")); 512 } 513 514 /* by default, be quiet */ 515 if (!passt->has_quiet || passt->quiet) { 516 g_ptr_array_add(args, g_strdup("--quiet")); 517 } 518 519 if (passt->has_mtu) { 520 g_ptr_array_add(args, g_strdup("--mtu")); 521 g_ptr_array_add(args, g_strdup_printf("%"PRId64, passt->mtu)); 522 } 523 524 if (passt->address) { 525 g_ptr_array_add(args, g_strdup("--address")); 526 g_ptr_array_add(args, g_strdup(passt->address)); 527 } 528 529 if (passt->netmask) { 530 g_ptr_array_add(args, g_strdup("--netmask")); 531 g_ptr_array_add(args, g_strdup(passt->netmask)); 532 } 533 534 if (passt->mac) { 535 g_ptr_array_add(args, g_strdup("--mac-addr")); 536 g_ptr_array_add(args, g_strdup(passt->mac)); 537 } 538 539 if (passt->gateway) { 540 g_ptr_array_add(args, g_strdup("--gateway")); 541 g_ptr_array_add(args, g_strdup(passt->gateway)); 542 } 543 544 if (passt->interface) { 545 g_ptr_array_add(args, g_strdup("--interface")); 546 g_ptr_array_add(args, g_strdup(passt->interface)); 547 } 548 549 if (passt->outbound) { 550 g_ptr_array_add(args, g_strdup("--outbound")); 551 g_ptr_array_add(args, g_strdup(passt->outbound)); 552 } 553 554 if (passt->outbound_if4) { 555 g_ptr_array_add(args, g_strdup("--outbound-if4")); 556 g_ptr_array_add(args, g_strdup(passt->outbound_if4)); 557 } 558 559 if (passt->outbound_if6) { 560 g_ptr_array_add(args, g_strdup("--outbound-if6")); 561 g_ptr_array_add(args, g_strdup(passt->outbound_if6)); 562 } 563 564 if (passt->dns) { 565 g_ptr_array_add(args, g_strdup("--dns")); 566 g_ptr_array_add(args, g_strdup(passt->dns)); 567 } 568 if (passt->fqdn) { 569 g_ptr_array_add(args, g_strdup("--fqdn")); 570 g_ptr_array_add(args, g_strdup(passt->fqdn)); 571 } 572 573 if (passt->has_dhcp_dns && !passt->dhcp_dns) { 574 g_ptr_array_add(args, g_strdup("--no-dhcp-dns")); 575 } 576 577 if (passt->has_dhcp_search && !passt->dhcp_search) { 578 g_ptr_array_add(args, g_strdup("--no-dhcp-search")); 579 } 580 581 if (passt->map_host_loopback) { 582 g_ptr_array_add(args, g_strdup("--map-host-loopback")); 583 g_ptr_array_add(args, g_strdup(passt->map_host_loopback)); 584 } 585 586 if (passt->map_guest_addr) { 587 g_ptr_array_add(args, g_strdup("--map-guest-addr")); 588 g_ptr_array_add(args, g_strdup(passt->map_guest_addr)); 589 } 590 591 if (passt->dns_forward) { 592 g_ptr_array_add(args, g_strdup("--dns-forward")); 593 g_ptr_array_add(args, g_strdup(passt->dns_forward)); 594 } 595 596 if (passt->dns_host) { 597 g_ptr_array_add(args, g_strdup("--dns-host")); 598 g_ptr_array_add(args, g_strdup(passt->dns_host)); 599 } 600 601 if (passt->has_tcp && !passt->tcp) { 602 g_ptr_array_add(args, g_strdup("--no-tcp")); 603 } 604 605 if (passt->has_udp && !passt->udp) { 606 g_ptr_array_add(args, g_strdup("--no-udp")); 607 } 608 609 if (passt->has_icmp && !passt->icmp) { 610 g_ptr_array_add(args, g_strdup("--no-icmp")); 611 } 612 613 if (passt->has_dhcp && !passt->dhcp) { 614 g_ptr_array_add(args, g_strdup("--no-dhcp")); 615 } 616 617 if (passt->has_ndp && !passt->ndp) { 618 g_ptr_array_add(args, g_strdup("--no-ndp")); 619 } 620 if (passt->has_dhcpv6 && !passt->dhcpv6) { 621 g_ptr_array_add(args, g_strdup("--no-dhcpv6")); 622 } 623 624 if (passt->has_ra && !passt->ra) { 625 g_ptr_array_add(args, g_strdup("--no-ra")); 626 } 627 628 if (passt->has_freebind && passt->freebind) { 629 g_ptr_array_add(args, g_strdup("--freebind")); 630 } 631 632 if (passt->has_ipv4 && !passt->ipv4) { 633 g_ptr_array_add(args, g_strdup("--ipv6-only")); 634 } 635 636 if (passt->has_ipv6 && !passt->ipv6) { 637 g_ptr_array_add(args, g_strdup("--ipv4-only")); 638 } 639 640 if (passt->has_search && passt->search) { 641 const StringList *list = passt->search; 642 GString *domains = g_string_new(list->value->str); 643 644 list = list->next; 645 while (list) { 646 g_string_append(domains, " "); 647 g_string_append(domains, list->value->str); 648 list = list->next; 649 } 650 651 g_ptr_array_add(args, g_strdup("--search")); 652 g_ptr_array_add(args, g_string_free(domains, FALSE)); 653 } 654 655 if (passt->has_tcp_ports && passt->tcp_ports) { 656 const StringList *list = passt->tcp_ports; 657 GString *tcp_ports = g_string_new(list->value->str); 658 659 list = list->next; 660 while (list) { 661 g_string_append(tcp_ports, ","); 662 g_string_append(tcp_ports, list->value->str); 663 list = list->next; 664 } 665 666 g_ptr_array_add(args, g_strdup("--tcp-ports")); 667 g_ptr_array_add(args, g_string_free(tcp_ports, FALSE)); 668 } 669 670 if (passt->has_udp_ports && passt->udp_ports) { 671 const StringList *list = passt->udp_ports; 672 GString *udp_ports = g_string_new(list->value->str); 673 674 list = list->next; 675 while (list) { 676 g_string_append(udp_ports, ","); 677 g_string_append(udp_ports, list->value->str); 678 list = list->next; 679 } 680 681 g_ptr_array_add(args, g_strdup("--udp-ports")); 682 g_ptr_array_add(args, g_string_free(udp_ports, FALSE)); 683 } 684 685 if (passt->has_param && passt->param) { 686 const StringList *list = passt->param; 687 688 while (list) { 689 g_ptr_array_add(args, g_strdup(list->value->str)); 690 list = list->next; 691 } 692 } 693 694 /* provide a pid file to be able to kil passt on exit */ 695 g_ptr_array_add(args, g_strdup("--pid")); 696 g_ptr_array_add(args, g_strdup(pidfile)); 697 698 /* g_subprocess_launcher_take_fd() will set the socket on fd 3 */ 699 g_ptr_array_add(args, g_strdup("--fd")); 700 g_ptr_array_add(args, g_strdup("3")); 701 702 g_ptr_array_add(args, NULL); 703 704 return args; 705 } 706 707 int net_init_passt(const Netdev *netdev, const char *name, 708 NetClientState *peer, Error **errp) 709 { 710 g_autoptr(GError) error = NULL; 711 NetClientState *nc; 712 NetPasstState *s; 713 GPtrArray *args; 714 gchar *pidfile; 715 int pidfd; 716 717 assert(netdev->type == NET_CLIENT_DRIVER_PASST); 718 719 pidfd = g_file_open_tmp("passt-XXXXXX.pid", &pidfile, &error); 720 if (pidfd == -1) { 721 error_setg(errp, "Failed to create temporary file: %s", error->message); 722 return -1; 723 } 724 close(pidfd); 725 726 args = net_passt_decode_args(&netdev->u.passt, pidfile, errp); 727 if (args == NULL) { 728 g_free(pidfile); 729 return -1; 730 } 731 732 nc = qemu_new_net_client(&net_passt_info, peer, "passt", name); 733 s = DO_UPCAST(NetPasstState, data.nc, nc); 734 735 s->args = args; 736 s->pidfile = pidfile; 737 738 if (netdev->u.passt.has_vhost_user && netdev->u.passt.vhost_user) { 739 if (net_passt_vhost_user_init(s, errp) == -1) { 740 qemu_del_net_client(nc); 741 return -1; 742 } 743 744 return 0; 745 } 746 747 if (net_passt_stream_start(s, errp) == -1) { 748 qemu_del_net_client(nc); 749 return -1; 750 } 751 752 return 0; 753 } 754