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