xref: /openbmc/qemu/net/passt.c (revision 2ed74e3c005f236acbf7d85d396a653bfb004a17)
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