xref: /openbmc/qemu/net/passt.c (revision 0828b374c6568eecdb5e5389986efd99898aa822)
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 
net_passt_cleanup(NetClientState * nc)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 
net_passt_receive(NetClientState * nc,const uint8_t * buf,size_t size)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 
net_passt_send(QIOChannel * ioc,GIOCondition condition,gpointer data)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 = NULL;
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
passt_set_vnet_endianness(NetClientState * nc,bool enable)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 
passt_has_vnet_hdr(NetClientState * nc)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 
passt_has_ufo(NetClientState * nc)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 
passt_check_peer_type(NetClientState * nc,ObjectClass * oc,Error ** errp)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 
passt_get_vhost_net(NetClientState * nc)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 
passt_get_acked_features(NetClientState * nc)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 
passt_save_acked_features(NetClientState * nc)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 
net_passt_client_connected(QIOTask * task,gpointer opaque)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 
net_passt_start_daemon(NetPasstState * s,int sock,Error ** errp)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 
net_passt_stream_start(NetPasstState * s,Error ** errp)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
passt_vhost_user_watch(void * do_not_use,GIOCondition cond,void * opaque)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 
chr_closed_bh(void * opaque)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 
passt_vhost_user_stop(NetPasstState * s)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 
passt_vhost_user_start(NetPasstState * s,VhostUserState * be)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 
passt_vhost_user_event(void * opaque,QEMUChrEvent event)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 
net_passt_vhost_user_init(NetPasstState * s,Error ** errp)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
net_passt_vhost_user_init(NetPasstState * s,Error ** errp)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 
net_passt_decode_args(const NetDevPasstOptions * passt,gchar * pidfile,Error ** errp)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 
net_init_passt(const Netdev * netdev,const char * name,NetClientState * peer,Error ** errp)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