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