xref: /openbmc/qemu/net/passt.c (revision da703b06a52bfb5fe1a77b0eddbb8d68d3f70762)
1854ee02bSLaurent Vivier /*
2854ee02bSLaurent Vivier  * passt network backend
3854ee02bSLaurent Vivier  *
4854ee02bSLaurent Vivier  * Copyright Red Hat
5854ee02bSLaurent Vivier  *
6854ee02bSLaurent Vivier  * SPDX-License-Identifier: GPL-2.0-or-later
7854ee02bSLaurent Vivier  */
8854ee02bSLaurent Vivier #include "qemu/osdep.h"
9854ee02bSLaurent Vivier #include <glib/gstdio.h>
10*da703b06SLaurent Vivier #include "qemu/error-report.h"
11854ee02bSLaurent Vivier #include <gio/gio.h>
12854ee02bSLaurent Vivier #include "net/net.h"
13854ee02bSLaurent Vivier #include "clients.h"
14854ee02bSLaurent Vivier #include "qapi/error.h"
15854ee02bSLaurent Vivier #include "io/net-listener.h"
16*da703b06SLaurent Vivier #include "chardev/char-fe.h"
17*da703b06SLaurent Vivier #include "net/vhost_net.h"
18*da703b06SLaurent Vivier #include "hw/virtio/vhost.h"
19*da703b06SLaurent Vivier #include "hw/virtio/vhost-user.h"
20*da703b06SLaurent Vivier #include "standard-headers/linux/virtio_net.h"
21854ee02bSLaurent Vivier #include "stream_data.h"
22854ee02bSLaurent Vivier 
23*da703b06SLaurent Vivier #ifdef CONFIG_VHOST_USER
24*da703b06SLaurent Vivier static const int user_feature_bits[] = {
25*da703b06SLaurent Vivier     VIRTIO_F_NOTIFY_ON_EMPTY,
26*da703b06SLaurent Vivier     VIRTIO_F_NOTIFICATION_DATA,
27*da703b06SLaurent Vivier     VIRTIO_RING_F_INDIRECT_DESC,
28*da703b06SLaurent Vivier     VIRTIO_RING_F_EVENT_IDX,
29*da703b06SLaurent Vivier 
30*da703b06SLaurent Vivier     VIRTIO_F_ANY_LAYOUT,
31*da703b06SLaurent Vivier     VIRTIO_F_VERSION_1,
32*da703b06SLaurent Vivier     VIRTIO_NET_F_CSUM,
33*da703b06SLaurent Vivier     VIRTIO_NET_F_GUEST_CSUM,
34*da703b06SLaurent Vivier     VIRTIO_NET_F_GSO,
35*da703b06SLaurent Vivier     VIRTIO_NET_F_GUEST_TSO4,
36*da703b06SLaurent Vivier     VIRTIO_NET_F_GUEST_TSO6,
37*da703b06SLaurent Vivier     VIRTIO_NET_F_GUEST_ECN,
38*da703b06SLaurent Vivier     VIRTIO_NET_F_GUEST_UFO,
39*da703b06SLaurent Vivier     VIRTIO_NET_F_HOST_TSO4,
40*da703b06SLaurent Vivier     VIRTIO_NET_F_HOST_TSO6,
41*da703b06SLaurent Vivier     VIRTIO_NET_F_HOST_ECN,
42*da703b06SLaurent Vivier     VIRTIO_NET_F_HOST_UFO,
43*da703b06SLaurent Vivier     VIRTIO_NET_F_MRG_RXBUF,
44*da703b06SLaurent Vivier     VIRTIO_NET_F_MTU,
45*da703b06SLaurent Vivier     VIRTIO_F_IOMMU_PLATFORM,
46*da703b06SLaurent Vivier     VIRTIO_F_RING_PACKED,
47*da703b06SLaurent Vivier     VIRTIO_F_RING_RESET,
48*da703b06SLaurent Vivier     VIRTIO_F_IN_ORDER,
49*da703b06SLaurent Vivier     VIRTIO_NET_F_RSS,
50*da703b06SLaurent Vivier     VIRTIO_NET_F_RSC_EXT,
51*da703b06SLaurent Vivier     VIRTIO_NET_F_HASH_REPORT,
52*da703b06SLaurent Vivier     VIRTIO_NET_F_GUEST_USO4,
53*da703b06SLaurent Vivier     VIRTIO_NET_F_GUEST_USO6,
54*da703b06SLaurent Vivier     VIRTIO_NET_F_HOST_USO,
55*da703b06SLaurent Vivier 
56*da703b06SLaurent Vivier     /* This bit implies RARP isn't sent by QEMU out of band */
57*da703b06SLaurent Vivier     VIRTIO_NET_F_GUEST_ANNOUNCE,
58*da703b06SLaurent Vivier 
59*da703b06SLaurent Vivier     VIRTIO_NET_F_MQ,
60*da703b06SLaurent Vivier 
61*da703b06SLaurent Vivier     VHOST_INVALID_FEATURE_BIT
62*da703b06SLaurent Vivier };
63*da703b06SLaurent Vivier #endif
64*da703b06SLaurent Vivier 
65854ee02bSLaurent Vivier typedef struct NetPasstState {
66854ee02bSLaurent Vivier     NetStreamData data;
67854ee02bSLaurent Vivier     GPtrArray *args;
68854ee02bSLaurent Vivier     gchar *pidfile;
69854ee02bSLaurent Vivier     pid_t pid;
70*da703b06SLaurent Vivier #ifdef CONFIG_VHOST_USER
71*da703b06SLaurent Vivier     /* vhost user */
72*da703b06SLaurent Vivier     VhostUserState *vhost_user;
73*da703b06SLaurent Vivier     VHostNetState *vhost_net;
74*da703b06SLaurent Vivier     CharBackend vhost_chr;
75*da703b06SLaurent Vivier     guint vhost_watch;
76*da703b06SLaurent Vivier     uint64_t acked_features;
77*da703b06SLaurent Vivier     bool started;
78*da703b06SLaurent Vivier #endif
79854ee02bSLaurent Vivier } NetPasstState;
80854ee02bSLaurent Vivier 
81854ee02bSLaurent Vivier static int net_passt_stream_start(NetPasstState *s, Error **errp);
82854ee02bSLaurent Vivier 
83854ee02bSLaurent Vivier static void net_passt_cleanup(NetClientState *nc)
84854ee02bSLaurent Vivier {
85854ee02bSLaurent Vivier     NetPasstState *s = DO_UPCAST(NetPasstState, data.nc, nc);
86854ee02bSLaurent Vivier 
87*da703b06SLaurent Vivier #ifdef CONFIG_VHOST_USER
88*da703b06SLaurent Vivier     if (s->vhost_net) {
89*da703b06SLaurent Vivier         vhost_net_cleanup(s->vhost_net);
90*da703b06SLaurent Vivier         g_free(s->vhost_net);
91*da703b06SLaurent Vivier         s->vhost_net = NULL;
92*da703b06SLaurent Vivier     }
93*da703b06SLaurent Vivier     if (s->vhost_watch) {
94*da703b06SLaurent Vivier         g_source_remove(s->vhost_watch);
95*da703b06SLaurent Vivier         s->vhost_watch = 0;
96*da703b06SLaurent Vivier     }
97*da703b06SLaurent Vivier     qemu_chr_fe_deinit(&s->vhost_chr, true);
98*da703b06SLaurent Vivier     if (s->vhost_user) {
99*da703b06SLaurent Vivier         vhost_user_cleanup(s->vhost_user);
100*da703b06SLaurent Vivier         g_free(s->vhost_user);
101*da703b06SLaurent Vivier         s->vhost_user = NULL;
102*da703b06SLaurent Vivier     }
103*da703b06SLaurent Vivier #endif
104*da703b06SLaurent Vivier 
105854ee02bSLaurent Vivier     kill(s->pid, SIGTERM);
106854ee02bSLaurent Vivier     g_remove(s->pidfile);
107854ee02bSLaurent Vivier     g_free(s->pidfile);
108854ee02bSLaurent Vivier     g_ptr_array_free(s->args, TRUE);
109854ee02bSLaurent Vivier }
110854ee02bSLaurent Vivier 
111854ee02bSLaurent Vivier static ssize_t net_passt_receive(NetClientState *nc, const uint8_t *buf,
112854ee02bSLaurent Vivier                                   size_t size)
113854ee02bSLaurent Vivier {
114854ee02bSLaurent Vivier     NetStreamData *d = DO_UPCAST(NetStreamData, nc, nc);
115854ee02bSLaurent Vivier 
116854ee02bSLaurent Vivier     return net_stream_data_receive(d, buf, size);
117854ee02bSLaurent Vivier }
118854ee02bSLaurent Vivier 
119854ee02bSLaurent Vivier static gboolean net_passt_send(QIOChannel *ioc, GIOCondition condition,
120854ee02bSLaurent Vivier                                 gpointer data)
121854ee02bSLaurent Vivier {
122854ee02bSLaurent Vivier     if (net_stream_data_send(ioc, condition, data) == G_SOURCE_REMOVE) {
123854ee02bSLaurent Vivier         NetPasstState *s = DO_UPCAST(NetPasstState, data, data);
124854ee02bSLaurent Vivier         Error *error;
125854ee02bSLaurent Vivier 
126854ee02bSLaurent Vivier         /* we need to restart passt */
127854ee02bSLaurent Vivier         kill(s->pid, SIGTERM);
128854ee02bSLaurent Vivier         if (net_passt_stream_start(s, &error) == -1) {
129854ee02bSLaurent Vivier             error_report_err(error);
130854ee02bSLaurent Vivier         }
131854ee02bSLaurent Vivier 
132854ee02bSLaurent Vivier         return G_SOURCE_REMOVE;
133854ee02bSLaurent Vivier     }
134854ee02bSLaurent Vivier 
135854ee02bSLaurent Vivier     return G_SOURCE_CONTINUE;
136854ee02bSLaurent Vivier }
137854ee02bSLaurent Vivier 
138*da703b06SLaurent Vivier #ifdef CONFIG_VHOST_USER
139*da703b06SLaurent Vivier static int passt_set_vnet_endianness(NetClientState *nc, bool enable)
140*da703b06SLaurent Vivier {
141*da703b06SLaurent Vivier     assert(nc->info->type == NET_CLIENT_DRIVER_PASST);
142*da703b06SLaurent Vivier 
143*da703b06SLaurent Vivier     return 0;
144*da703b06SLaurent Vivier }
145*da703b06SLaurent Vivier 
146*da703b06SLaurent Vivier static bool passt_has_vnet_hdr(NetClientState *nc)
147*da703b06SLaurent Vivier {
148*da703b06SLaurent Vivier     NetPasstState *s = DO_UPCAST(NetPasstState, data.nc, nc);
149*da703b06SLaurent Vivier 
150*da703b06SLaurent Vivier     assert(nc->info->type == NET_CLIENT_DRIVER_PASST);
151*da703b06SLaurent Vivier 
152*da703b06SLaurent Vivier     return s->vhost_user != NULL;
153*da703b06SLaurent Vivier }
154*da703b06SLaurent Vivier 
155*da703b06SLaurent Vivier static bool passt_has_ufo(NetClientState *nc)
156*da703b06SLaurent Vivier {
157*da703b06SLaurent Vivier     NetPasstState *s = DO_UPCAST(NetPasstState, data.nc, nc);
158*da703b06SLaurent Vivier 
159*da703b06SLaurent Vivier     assert(nc->info->type == NET_CLIENT_DRIVER_PASST);
160*da703b06SLaurent Vivier 
161*da703b06SLaurent Vivier     return s->vhost_user != NULL;
162*da703b06SLaurent Vivier }
163*da703b06SLaurent Vivier 
164*da703b06SLaurent Vivier static bool passt_check_peer_type(NetClientState *nc, ObjectClass *oc,
165*da703b06SLaurent Vivier                                              Error **errp)
166*da703b06SLaurent Vivier {
167*da703b06SLaurent Vivier     NetPasstState *s = DO_UPCAST(NetPasstState, data.nc, nc);
168*da703b06SLaurent Vivier     const char *driver = object_class_get_name(oc);
169*da703b06SLaurent Vivier 
170*da703b06SLaurent Vivier     assert(nc->info->type == NET_CLIENT_DRIVER_PASST);
171*da703b06SLaurent Vivier 
172*da703b06SLaurent Vivier     if (s->vhost_user == NULL) {
173*da703b06SLaurent Vivier         return true;
174*da703b06SLaurent Vivier     }
175*da703b06SLaurent Vivier 
176*da703b06SLaurent Vivier     if (!g_str_has_prefix(driver, "virtio-net-")) {
177*da703b06SLaurent Vivier         error_setg(errp, "vhost-user requires frontend driver virtio-net-*");
178*da703b06SLaurent Vivier         return false;
179*da703b06SLaurent Vivier     }
180*da703b06SLaurent Vivier 
181*da703b06SLaurent Vivier     return true;
182*da703b06SLaurent Vivier }
183*da703b06SLaurent Vivier 
184*da703b06SLaurent Vivier static struct vhost_net *passt_get_vhost_net(NetClientState *nc)
185*da703b06SLaurent Vivier {
186*da703b06SLaurent Vivier     NetPasstState *s = DO_UPCAST(NetPasstState, data.nc, nc);
187*da703b06SLaurent Vivier 
188*da703b06SLaurent Vivier     assert(nc->info->type == NET_CLIENT_DRIVER_PASST);
189*da703b06SLaurent Vivier 
190*da703b06SLaurent Vivier     return s->vhost_net;
191*da703b06SLaurent Vivier }
192*da703b06SLaurent Vivier 
193*da703b06SLaurent Vivier static uint64_t passt_get_acked_features(NetClientState *nc)
194*da703b06SLaurent Vivier {
195*da703b06SLaurent Vivier     NetPasstState *s = DO_UPCAST(NetPasstState, data.nc, nc);
196*da703b06SLaurent Vivier 
197*da703b06SLaurent Vivier     assert(nc->info->type == NET_CLIENT_DRIVER_PASST);
198*da703b06SLaurent Vivier 
199*da703b06SLaurent Vivier     return s->acked_features;
200*da703b06SLaurent Vivier }
201*da703b06SLaurent Vivier 
202*da703b06SLaurent Vivier static void passt_save_acked_features(NetClientState *nc)
203*da703b06SLaurent Vivier {
204*da703b06SLaurent Vivier     NetPasstState *s = DO_UPCAST(NetPasstState, data.nc, nc);
205*da703b06SLaurent Vivier 
206*da703b06SLaurent Vivier     assert(nc->info->type == NET_CLIENT_DRIVER_PASST);
207*da703b06SLaurent Vivier 
208*da703b06SLaurent Vivier     if (s->vhost_net) {
209*da703b06SLaurent Vivier         uint64_t features = vhost_net_get_acked_features(s->vhost_net);
210*da703b06SLaurent Vivier         if (features) {
211*da703b06SLaurent Vivier             s->acked_features = features;
212*da703b06SLaurent Vivier         }
213*da703b06SLaurent Vivier     }
214*da703b06SLaurent Vivier }
215*da703b06SLaurent Vivier #endif
216*da703b06SLaurent Vivier 
217854ee02bSLaurent Vivier static NetClientInfo net_passt_info = {
218854ee02bSLaurent Vivier     .type = NET_CLIENT_DRIVER_PASST,
219854ee02bSLaurent Vivier     .size = sizeof(NetPasstState),
220854ee02bSLaurent Vivier     .receive = net_passt_receive,
221854ee02bSLaurent Vivier     .cleanup = net_passt_cleanup,
222*da703b06SLaurent Vivier #ifdef CONFIG_VHOST_USER
223*da703b06SLaurent Vivier     .has_vnet_hdr = passt_has_vnet_hdr,
224*da703b06SLaurent Vivier     .has_ufo = passt_has_ufo,
225*da703b06SLaurent Vivier     .set_vnet_be = passt_set_vnet_endianness,
226*da703b06SLaurent Vivier     .set_vnet_le = passt_set_vnet_endianness,
227*da703b06SLaurent Vivier     .check_peer_type = passt_check_peer_type,
228*da703b06SLaurent Vivier     .get_vhost_net = passt_get_vhost_net,
229*da703b06SLaurent Vivier #endif
230854ee02bSLaurent Vivier };
231854ee02bSLaurent Vivier 
232854ee02bSLaurent Vivier static void net_passt_client_connected(QIOTask *task, gpointer opaque)
233854ee02bSLaurent Vivier {
234854ee02bSLaurent Vivier     NetPasstState *s = opaque;
235854ee02bSLaurent Vivier 
236854ee02bSLaurent Vivier     if (net_stream_data_client_connected(task, &s->data) == 0) {
237854ee02bSLaurent Vivier         qemu_set_info_str(&s->data.nc, "stream,connected to pid %d", s->pid);
238854ee02bSLaurent Vivier     }
239854ee02bSLaurent Vivier }
240854ee02bSLaurent Vivier 
241854ee02bSLaurent Vivier static int net_passt_start_daemon(NetPasstState *s, int sock, Error **errp)
242854ee02bSLaurent Vivier {
243854ee02bSLaurent Vivier     g_autoptr(GSubprocess) daemon = NULL;
244854ee02bSLaurent Vivier     g_autofree gchar *contents = NULL;
245854ee02bSLaurent Vivier     g_autoptr(GError) error = NULL;
246854ee02bSLaurent Vivier     GSubprocessLauncher *launcher;
247854ee02bSLaurent Vivier 
248854ee02bSLaurent Vivier     qemu_set_info_str(&s->data.nc, "launching passt");
249854ee02bSLaurent Vivier 
250854ee02bSLaurent Vivier     launcher = g_subprocess_launcher_new(G_SUBPROCESS_FLAGS_NONE);
251854ee02bSLaurent Vivier     g_subprocess_launcher_take_fd(launcher, sock, 3);
252854ee02bSLaurent Vivier 
253854ee02bSLaurent Vivier     daemon =  g_subprocess_launcher_spawnv(launcher,
254854ee02bSLaurent Vivier                                            (const gchar *const *)s->args->pdata,
255854ee02bSLaurent Vivier                                            &error);
256854ee02bSLaurent Vivier     g_object_unref(launcher);
257854ee02bSLaurent Vivier 
258854ee02bSLaurent Vivier     if (!daemon) {
259854ee02bSLaurent Vivier         error_setg(errp, "Error creating daemon: %s", error->message);
260854ee02bSLaurent Vivier         return -1;
261854ee02bSLaurent Vivier     }
262854ee02bSLaurent Vivier 
263854ee02bSLaurent Vivier     if (!g_subprocess_wait(daemon, NULL, &error)) {
264854ee02bSLaurent Vivier         error_setg(errp, "Error waiting for daemon: %s", error->message);
265854ee02bSLaurent Vivier         return -1;
266854ee02bSLaurent Vivier     }
267854ee02bSLaurent Vivier 
268854ee02bSLaurent Vivier     if (g_subprocess_get_if_exited(daemon) &&
269854ee02bSLaurent Vivier         g_subprocess_get_exit_status(daemon)) {
270854ee02bSLaurent Vivier         return -1;
271854ee02bSLaurent Vivier     }
272854ee02bSLaurent Vivier 
273854ee02bSLaurent Vivier     if (!g_file_get_contents(s->pidfile, &contents, NULL, &error)) {
274854ee02bSLaurent Vivier         error_setg(errp, "Cannot read passt pid: %s", error->message);
275854ee02bSLaurent Vivier         return -1;
276854ee02bSLaurent Vivier     }
277854ee02bSLaurent Vivier 
278854ee02bSLaurent Vivier     s->pid = (pid_t)g_ascii_strtoll(contents, NULL, 10);
279854ee02bSLaurent Vivier     if (s->pid <= 0) {
280854ee02bSLaurent Vivier         error_setg(errp, "File '%s' did not contain a valid PID.", s->pidfile);
281854ee02bSLaurent Vivier         return -1;
282854ee02bSLaurent Vivier     }
283854ee02bSLaurent Vivier 
284854ee02bSLaurent Vivier     return 0;
285854ee02bSLaurent Vivier }
286854ee02bSLaurent Vivier 
287854ee02bSLaurent Vivier static int net_passt_stream_start(NetPasstState *s, Error **errp)
288854ee02bSLaurent Vivier {
289854ee02bSLaurent Vivier     QIOChannelSocket *sioc;
290854ee02bSLaurent Vivier     SocketAddress *addr;
291854ee02bSLaurent Vivier     int sv[2];
292854ee02bSLaurent Vivier 
293854ee02bSLaurent Vivier     if (socketpair(PF_UNIX, SOCK_STREAM, 0, sv) == -1) {
294854ee02bSLaurent Vivier         error_setg_errno(errp, errno, "socketpair() failed");
295854ee02bSLaurent Vivier         return -1;
296854ee02bSLaurent Vivier     }
297854ee02bSLaurent Vivier 
298854ee02bSLaurent Vivier     /* connect to passt */
299854ee02bSLaurent Vivier     qemu_set_info_str(&s->data.nc, "connecting to passt");
300854ee02bSLaurent Vivier 
301854ee02bSLaurent Vivier     /* create socket channel */
302854ee02bSLaurent Vivier     sioc = qio_channel_socket_new();
303854ee02bSLaurent Vivier     s->data.ioc = QIO_CHANNEL(sioc);
304854ee02bSLaurent Vivier     s->data.nc.link_down = true;
305854ee02bSLaurent Vivier     s->data.send = net_passt_send;
306854ee02bSLaurent Vivier 
307854ee02bSLaurent Vivier     addr = g_new0(SocketAddress, 1);
308854ee02bSLaurent Vivier     addr->type = SOCKET_ADDRESS_TYPE_FD;
309854ee02bSLaurent Vivier     addr->u.fd.str = g_strdup_printf("%d", sv[0]);
310854ee02bSLaurent Vivier 
311854ee02bSLaurent Vivier     qio_channel_socket_connect_async(sioc, addr,
312854ee02bSLaurent Vivier                                      net_passt_client_connected, s,
313854ee02bSLaurent Vivier                                      NULL, NULL);
314854ee02bSLaurent Vivier 
315854ee02bSLaurent Vivier     qapi_free_SocketAddress(addr);
316854ee02bSLaurent Vivier 
317854ee02bSLaurent Vivier     /* start passt */
318854ee02bSLaurent Vivier     if (net_passt_start_daemon(s, sv[1], errp) == -1) {
319854ee02bSLaurent Vivier         close(sv[0]);
320854ee02bSLaurent Vivier         close(sv[1]);
321854ee02bSLaurent Vivier         return -1;
322854ee02bSLaurent Vivier     }
323854ee02bSLaurent Vivier     close(sv[1]);
324854ee02bSLaurent Vivier 
325854ee02bSLaurent Vivier     return 0;
326854ee02bSLaurent Vivier }
327854ee02bSLaurent Vivier 
328*da703b06SLaurent Vivier #ifdef CONFIG_VHOST_USER
329*da703b06SLaurent Vivier static gboolean passt_vhost_user_watch(void *do_not_use, GIOCondition cond,
330*da703b06SLaurent Vivier                                        void *opaque)
331*da703b06SLaurent Vivier {
332*da703b06SLaurent Vivier     NetPasstState *s = opaque;
333*da703b06SLaurent Vivier 
334*da703b06SLaurent Vivier     qemu_chr_fe_disconnect(&s->vhost_chr);
335*da703b06SLaurent Vivier 
336*da703b06SLaurent Vivier     return G_SOURCE_CONTINUE;
337*da703b06SLaurent Vivier }
338*da703b06SLaurent Vivier 
339*da703b06SLaurent Vivier static void passt_vhost_user_event(void *opaque, QEMUChrEvent event);
340*da703b06SLaurent Vivier 
341*da703b06SLaurent Vivier static void chr_closed_bh(void *opaque)
342*da703b06SLaurent Vivier {
343*da703b06SLaurent Vivier     NetPasstState *s = opaque;
344*da703b06SLaurent Vivier 
345*da703b06SLaurent Vivier     passt_save_acked_features(&s->data.nc);
346*da703b06SLaurent Vivier 
347*da703b06SLaurent Vivier     net_client_set_link(&(NetClientState *){ &s->data.nc }, 1, false);
348*da703b06SLaurent Vivier 
349*da703b06SLaurent Vivier     qemu_chr_fe_set_handlers(&s->vhost_chr, NULL, NULL, passt_vhost_user_event,
350*da703b06SLaurent Vivier                              NULL, s, NULL, true);
351*da703b06SLaurent Vivier }
352*da703b06SLaurent Vivier 
353*da703b06SLaurent Vivier static void passt_vhost_user_stop(NetPasstState *s)
354*da703b06SLaurent Vivier {
355*da703b06SLaurent Vivier     passt_save_acked_features(&s->data.nc);
356*da703b06SLaurent Vivier     vhost_net_cleanup(s->vhost_net);
357*da703b06SLaurent Vivier }
358*da703b06SLaurent Vivier 
359*da703b06SLaurent Vivier static int passt_vhost_user_start(NetPasstState *s, VhostUserState *be)
360*da703b06SLaurent Vivier {
361*da703b06SLaurent Vivier     struct vhost_net *net = NULL;
362*da703b06SLaurent Vivier     VhostNetOptions options;
363*da703b06SLaurent Vivier 
364*da703b06SLaurent Vivier     options.backend_type = VHOST_BACKEND_TYPE_USER;
365*da703b06SLaurent Vivier     options.net_backend = &s->data.nc;
366*da703b06SLaurent Vivier     options.opaque = be;
367*da703b06SLaurent Vivier     options.busyloop_timeout = 0;
368*da703b06SLaurent Vivier     options.nvqs = 2;
369*da703b06SLaurent Vivier     options.feature_bits = user_feature_bits;
370*da703b06SLaurent Vivier     options.max_tx_queue_size = VIRTQUEUE_MAX_SIZE;
371*da703b06SLaurent Vivier     options.get_acked_features = passt_get_acked_features;
372*da703b06SLaurent Vivier     options.save_acked_features = passt_save_acked_features;
373*da703b06SLaurent Vivier     options.is_vhost_user = true;
374*da703b06SLaurent Vivier 
375*da703b06SLaurent Vivier     net = vhost_net_init(&options);
376*da703b06SLaurent Vivier     if (!net) {
377*da703b06SLaurent Vivier         error_report("failed to init passt vhost_net");
378*da703b06SLaurent Vivier         goto err;
379*da703b06SLaurent Vivier     }
380*da703b06SLaurent Vivier 
381*da703b06SLaurent Vivier     if (s->vhost_net) {
382*da703b06SLaurent Vivier         vhost_net_cleanup(s->vhost_net);
383*da703b06SLaurent Vivier         g_free(s->vhost_net);
384*da703b06SLaurent Vivier     }
385*da703b06SLaurent Vivier     s->vhost_net = net;
386*da703b06SLaurent Vivier 
387*da703b06SLaurent Vivier     return 0;
388*da703b06SLaurent Vivier err:
389*da703b06SLaurent Vivier     if (net) {
390*da703b06SLaurent Vivier         vhost_net_cleanup(net);
391*da703b06SLaurent Vivier         g_free(net);
392*da703b06SLaurent Vivier     }
393*da703b06SLaurent Vivier     passt_vhost_user_stop(s);
394*da703b06SLaurent Vivier     return -1;
395*da703b06SLaurent Vivier }
396*da703b06SLaurent Vivier 
397*da703b06SLaurent Vivier static void passt_vhost_user_event(void *opaque, QEMUChrEvent event)
398*da703b06SLaurent Vivier {
399*da703b06SLaurent Vivier     NetPasstState *s = opaque;
400*da703b06SLaurent Vivier     Error *err = NULL;
401*da703b06SLaurent Vivier 
402*da703b06SLaurent Vivier     switch (event) {
403*da703b06SLaurent Vivier     case CHR_EVENT_OPENED:
404*da703b06SLaurent Vivier         if (passt_vhost_user_start(s, s->vhost_user) < 0) {
405*da703b06SLaurent Vivier             qemu_chr_fe_disconnect(&s->vhost_chr);
406*da703b06SLaurent Vivier             return;
407*da703b06SLaurent Vivier         }
408*da703b06SLaurent Vivier         s->vhost_watch = qemu_chr_fe_add_watch(&s->vhost_chr, G_IO_HUP,
409*da703b06SLaurent Vivier                                                passt_vhost_user_watch, s);
410*da703b06SLaurent Vivier         net_client_set_link(&(NetClientState *){ &s->data.nc }, 1, true);
411*da703b06SLaurent Vivier         s->started = true;
412*da703b06SLaurent Vivier         break;
413*da703b06SLaurent Vivier     case CHR_EVENT_CLOSED:
414*da703b06SLaurent Vivier         if (s->vhost_watch) {
415*da703b06SLaurent Vivier             AioContext *ctx = qemu_get_current_aio_context();
416*da703b06SLaurent Vivier 
417*da703b06SLaurent Vivier             g_source_remove(s->vhost_watch);
418*da703b06SLaurent Vivier             s->vhost_watch = 0;
419*da703b06SLaurent Vivier             qemu_chr_fe_set_handlers(&s->vhost_chr, NULL, NULL,  NULL, NULL,
420*da703b06SLaurent Vivier                                      NULL, NULL, false);
421*da703b06SLaurent Vivier 
422*da703b06SLaurent Vivier             aio_bh_schedule_oneshot(ctx, chr_closed_bh, s);
423*da703b06SLaurent Vivier         }
424*da703b06SLaurent Vivier         break;
425*da703b06SLaurent Vivier     case CHR_EVENT_BREAK:
426*da703b06SLaurent Vivier     case CHR_EVENT_MUX_IN:
427*da703b06SLaurent Vivier     case CHR_EVENT_MUX_OUT:
428*da703b06SLaurent Vivier         /* Ignore */
429*da703b06SLaurent Vivier         break;
430*da703b06SLaurent Vivier     }
431*da703b06SLaurent Vivier 
432*da703b06SLaurent Vivier     if (err) {
433*da703b06SLaurent Vivier         error_report_err(err);
434*da703b06SLaurent Vivier     }
435*da703b06SLaurent Vivier }
436*da703b06SLaurent Vivier 
437*da703b06SLaurent Vivier static int net_passt_vhost_user_init(NetPasstState *s, Error **errp)
438*da703b06SLaurent Vivier {
439*da703b06SLaurent Vivier     Chardev *chr;
440*da703b06SLaurent Vivier     int sv[2];
441*da703b06SLaurent Vivier 
442*da703b06SLaurent Vivier     if (socketpair(PF_UNIX, SOCK_STREAM, 0, sv) == -1) {
443*da703b06SLaurent Vivier         error_setg_errno(errp, errno, "socketpair() failed");
444*da703b06SLaurent Vivier         return -1;
445*da703b06SLaurent Vivier     }
446*da703b06SLaurent Vivier 
447*da703b06SLaurent Vivier     /* connect to passt */
448*da703b06SLaurent Vivier     qemu_set_info_str(&s->data.nc, "connecting to passt");
449*da703b06SLaurent Vivier 
450*da703b06SLaurent Vivier     /* create chardev */
451*da703b06SLaurent Vivier 
452*da703b06SLaurent Vivier     chr = CHARDEV(object_new(TYPE_CHARDEV_SOCKET));
453*da703b06SLaurent Vivier     if (!chr || qemu_chr_add_client(chr, sv[0]) == -1) {
454*da703b06SLaurent Vivier         object_unref(OBJECT(chr));
455*da703b06SLaurent Vivier         error_setg(errp, "Failed to make socket chardev");
456*da703b06SLaurent Vivier         goto err;
457*da703b06SLaurent Vivier     }
458*da703b06SLaurent Vivier 
459*da703b06SLaurent Vivier     s->vhost_user = g_new0(struct VhostUserState, 1);
460*da703b06SLaurent Vivier     if (!qemu_chr_fe_init(&s->vhost_chr, chr, errp) ||
461*da703b06SLaurent Vivier         !vhost_user_init(s->vhost_user, &s->vhost_chr, errp)) {
462*da703b06SLaurent Vivier         goto err;
463*da703b06SLaurent Vivier     }
464*da703b06SLaurent Vivier 
465*da703b06SLaurent Vivier     /* start passt */
466*da703b06SLaurent Vivier     if (net_passt_start_daemon(s, sv[1], errp) == -1) {
467*da703b06SLaurent Vivier         goto err;
468*da703b06SLaurent Vivier     }
469*da703b06SLaurent Vivier 
470*da703b06SLaurent Vivier     do {
471*da703b06SLaurent Vivier         if (qemu_chr_fe_wait_connected(&s->vhost_chr, errp) < 0) {
472*da703b06SLaurent Vivier             goto err;
473*da703b06SLaurent Vivier         }
474*da703b06SLaurent Vivier 
475*da703b06SLaurent Vivier         qemu_chr_fe_set_handlers(&s->vhost_chr, NULL, NULL,
476*da703b06SLaurent Vivier                                  passt_vhost_user_event, NULL, s, NULL,
477*da703b06SLaurent Vivier                                  true);
478*da703b06SLaurent Vivier     } while (!s->started);
479*da703b06SLaurent Vivier 
480*da703b06SLaurent Vivier     qemu_set_info_str(&s->data.nc, "vhost-user,connected to pid %d", s->pid);
481*da703b06SLaurent Vivier 
482*da703b06SLaurent Vivier     close(sv[1]);
483*da703b06SLaurent Vivier     return 0;
484*da703b06SLaurent Vivier err:
485*da703b06SLaurent Vivier     close(sv[0]);
486*da703b06SLaurent Vivier     close(sv[1]);
487*da703b06SLaurent Vivier 
488*da703b06SLaurent Vivier     return -1;
489*da703b06SLaurent Vivier }
490*da703b06SLaurent Vivier #else
491*da703b06SLaurent Vivier static int net_passt_vhost_user_init(NetPasstState *s, Error **errp)
492*da703b06SLaurent Vivier {
493*da703b06SLaurent Vivier     error_setg(errp, "vhost-user support has not been built");
494*da703b06SLaurent Vivier 
495*da703b06SLaurent Vivier     return -1;
496*da703b06SLaurent Vivier }
497*da703b06SLaurent Vivier #endif
498*da703b06SLaurent Vivier 
499854ee02bSLaurent Vivier static GPtrArray *net_passt_decode_args(const NetDevPasstOptions *passt,
500854ee02bSLaurent Vivier                                         gchar *pidfile, Error **errp)
501854ee02bSLaurent Vivier {
502854ee02bSLaurent Vivier     GPtrArray *args = g_ptr_array_new_with_free_func(g_free);
503854ee02bSLaurent Vivier 
504854ee02bSLaurent Vivier     if (passt->path) {
505854ee02bSLaurent Vivier         g_ptr_array_add(args, g_strdup(passt->path));
506854ee02bSLaurent Vivier     } else {
507854ee02bSLaurent Vivier         g_ptr_array_add(args, g_strdup("passt"));
508854ee02bSLaurent Vivier     }
509854ee02bSLaurent Vivier 
510*da703b06SLaurent Vivier     if (passt->has_vhost_user && passt->vhost_user) {
511*da703b06SLaurent Vivier         g_ptr_array_add(args, g_strdup("--vhost-user"));
512*da703b06SLaurent Vivier     }
513*da703b06SLaurent Vivier 
514854ee02bSLaurent Vivier     /* by default, be quiet */
515854ee02bSLaurent Vivier     if (!passt->has_quiet || passt->quiet) {
516854ee02bSLaurent Vivier         g_ptr_array_add(args, g_strdup("--quiet"));
517854ee02bSLaurent Vivier     }
518854ee02bSLaurent Vivier 
519854ee02bSLaurent Vivier     if (passt->has_mtu) {
520854ee02bSLaurent Vivier         g_ptr_array_add(args, g_strdup("--mtu"));
521854ee02bSLaurent Vivier         g_ptr_array_add(args, g_strdup_printf("%"PRId64, passt->mtu));
522854ee02bSLaurent Vivier     }
523854ee02bSLaurent Vivier 
524854ee02bSLaurent Vivier     if (passt->address) {
525854ee02bSLaurent Vivier         g_ptr_array_add(args, g_strdup("--address"));
526854ee02bSLaurent Vivier         g_ptr_array_add(args, g_strdup(passt->address));
527854ee02bSLaurent Vivier     }
528854ee02bSLaurent Vivier 
529854ee02bSLaurent Vivier     if (passt->netmask) {
530854ee02bSLaurent Vivier         g_ptr_array_add(args, g_strdup("--netmask"));
531854ee02bSLaurent Vivier         g_ptr_array_add(args, g_strdup(passt->netmask));
532854ee02bSLaurent Vivier     }
533854ee02bSLaurent Vivier 
534854ee02bSLaurent Vivier     if (passt->mac) {
535854ee02bSLaurent Vivier         g_ptr_array_add(args, g_strdup("--mac-addr"));
536854ee02bSLaurent Vivier         g_ptr_array_add(args, g_strdup(passt->mac));
537854ee02bSLaurent Vivier     }
538854ee02bSLaurent Vivier 
539854ee02bSLaurent Vivier     if (passt->gateway) {
540854ee02bSLaurent Vivier         g_ptr_array_add(args, g_strdup("--gateway"));
541854ee02bSLaurent Vivier         g_ptr_array_add(args, g_strdup(passt->gateway));
542854ee02bSLaurent Vivier     }
543854ee02bSLaurent Vivier 
544854ee02bSLaurent Vivier     if (passt->interface) {
545854ee02bSLaurent Vivier         g_ptr_array_add(args, g_strdup("--interface"));
546854ee02bSLaurent Vivier         g_ptr_array_add(args, g_strdup(passt->interface));
547854ee02bSLaurent Vivier     }
548854ee02bSLaurent Vivier 
549854ee02bSLaurent Vivier     if (passt->outbound) {
550854ee02bSLaurent Vivier         g_ptr_array_add(args, g_strdup("--outbound"));
551854ee02bSLaurent Vivier         g_ptr_array_add(args, g_strdup(passt->outbound));
552854ee02bSLaurent Vivier     }
553854ee02bSLaurent Vivier 
554854ee02bSLaurent Vivier     if (passt->outbound_if4) {
555854ee02bSLaurent Vivier         g_ptr_array_add(args, g_strdup("--outbound-if4"));
556854ee02bSLaurent Vivier         g_ptr_array_add(args, g_strdup(passt->outbound_if4));
557854ee02bSLaurent Vivier     }
558854ee02bSLaurent Vivier 
559854ee02bSLaurent Vivier     if (passt->outbound_if6) {
560854ee02bSLaurent Vivier         g_ptr_array_add(args, g_strdup("--outbound-if6"));
561854ee02bSLaurent Vivier         g_ptr_array_add(args, g_strdup(passt->outbound_if6));
562854ee02bSLaurent Vivier     }
563854ee02bSLaurent Vivier 
564854ee02bSLaurent Vivier     if (passt->dns) {
565854ee02bSLaurent Vivier         g_ptr_array_add(args, g_strdup("--dns"));
566854ee02bSLaurent Vivier         g_ptr_array_add(args, g_strdup(passt->dns));
567854ee02bSLaurent Vivier     }
568854ee02bSLaurent Vivier     if (passt->fqdn) {
569854ee02bSLaurent Vivier         g_ptr_array_add(args, g_strdup("--fqdn"));
570854ee02bSLaurent Vivier         g_ptr_array_add(args, g_strdup(passt->fqdn));
571854ee02bSLaurent Vivier     }
572854ee02bSLaurent Vivier 
573854ee02bSLaurent Vivier     if (passt->has_dhcp_dns && !passt->dhcp_dns) {
574854ee02bSLaurent Vivier         g_ptr_array_add(args, g_strdup("--no-dhcp-dns"));
575854ee02bSLaurent Vivier     }
576854ee02bSLaurent Vivier 
577854ee02bSLaurent Vivier     if (passt->has_dhcp_search && !passt->dhcp_search) {
578854ee02bSLaurent Vivier         g_ptr_array_add(args, g_strdup("--no-dhcp-search"));
579854ee02bSLaurent Vivier     }
580854ee02bSLaurent Vivier 
581854ee02bSLaurent Vivier     if (passt->map_host_loopback) {
582854ee02bSLaurent Vivier         g_ptr_array_add(args, g_strdup("--map-host-loopback"));
583854ee02bSLaurent Vivier         g_ptr_array_add(args, g_strdup(passt->map_host_loopback));
584854ee02bSLaurent Vivier     }
585854ee02bSLaurent Vivier 
586854ee02bSLaurent Vivier     if (passt->map_guest_addr) {
587854ee02bSLaurent Vivier         g_ptr_array_add(args, g_strdup("--map-guest-addr"));
588854ee02bSLaurent Vivier         g_ptr_array_add(args, g_strdup(passt->map_guest_addr));
589854ee02bSLaurent Vivier     }
590854ee02bSLaurent Vivier 
591854ee02bSLaurent Vivier     if (passt->dns_forward) {
592854ee02bSLaurent Vivier         g_ptr_array_add(args, g_strdup("--dns-forward"));
593854ee02bSLaurent Vivier         g_ptr_array_add(args, g_strdup(passt->dns_forward));
594854ee02bSLaurent Vivier     }
595854ee02bSLaurent Vivier 
596854ee02bSLaurent Vivier     if (passt->dns_host) {
597854ee02bSLaurent Vivier         g_ptr_array_add(args, g_strdup("--dns-host"));
598854ee02bSLaurent Vivier         g_ptr_array_add(args, g_strdup(passt->dns_host));
599854ee02bSLaurent Vivier     }
600854ee02bSLaurent Vivier 
601854ee02bSLaurent Vivier     if (passt->has_tcp && !passt->tcp) {
602854ee02bSLaurent Vivier         g_ptr_array_add(args, g_strdup("--no-tcp"));
603854ee02bSLaurent Vivier     }
604854ee02bSLaurent Vivier 
605854ee02bSLaurent Vivier     if (passt->has_udp && !passt->udp) {
606854ee02bSLaurent Vivier         g_ptr_array_add(args, g_strdup("--no-udp"));
607854ee02bSLaurent Vivier     }
608854ee02bSLaurent Vivier 
609854ee02bSLaurent Vivier     if (passt->has_icmp && !passt->icmp) {
610854ee02bSLaurent Vivier         g_ptr_array_add(args, g_strdup("--no-icmp"));
611854ee02bSLaurent Vivier     }
612854ee02bSLaurent Vivier 
613854ee02bSLaurent Vivier     if (passt->has_dhcp && !passt->dhcp) {
614854ee02bSLaurent Vivier         g_ptr_array_add(args, g_strdup("--no-dhcp"));
615854ee02bSLaurent Vivier     }
616854ee02bSLaurent Vivier 
617854ee02bSLaurent Vivier     if (passt->has_ndp && !passt->ndp) {
618854ee02bSLaurent Vivier         g_ptr_array_add(args, g_strdup("--no-ndp"));
619854ee02bSLaurent Vivier     }
620854ee02bSLaurent Vivier     if (passt->has_dhcpv6 && !passt->dhcpv6) {
621854ee02bSLaurent Vivier         g_ptr_array_add(args, g_strdup("--no-dhcpv6"));
622854ee02bSLaurent Vivier     }
623854ee02bSLaurent Vivier 
624854ee02bSLaurent Vivier     if (passt->has_ra && !passt->ra) {
625854ee02bSLaurent Vivier         g_ptr_array_add(args, g_strdup("--no-ra"));
626854ee02bSLaurent Vivier     }
627854ee02bSLaurent Vivier 
628854ee02bSLaurent Vivier     if (passt->has_freebind && passt->freebind) {
629854ee02bSLaurent Vivier         g_ptr_array_add(args, g_strdup("--freebind"));
630854ee02bSLaurent Vivier     }
631854ee02bSLaurent Vivier 
632854ee02bSLaurent Vivier     if (passt->has_ipv4 && !passt->ipv4) {
633854ee02bSLaurent Vivier         g_ptr_array_add(args, g_strdup("--ipv6-only"));
634854ee02bSLaurent Vivier     }
635854ee02bSLaurent Vivier 
636854ee02bSLaurent Vivier     if (passt->has_ipv6 && !passt->ipv6) {
637854ee02bSLaurent Vivier         g_ptr_array_add(args, g_strdup("--ipv4-only"));
638854ee02bSLaurent Vivier     }
639854ee02bSLaurent Vivier 
640854ee02bSLaurent Vivier     if (passt->has_search && passt->search) {
641854ee02bSLaurent Vivier         const StringList *list = passt->search;
642854ee02bSLaurent Vivier         GString *domains = g_string_new(list->value->str);
643854ee02bSLaurent Vivier 
644854ee02bSLaurent Vivier         list = list->next;
645854ee02bSLaurent Vivier         while (list) {
646854ee02bSLaurent Vivier             g_string_append(domains, " ");
647854ee02bSLaurent Vivier             g_string_append(domains, list->value->str);
648854ee02bSLaurent Vivier             list = list->next;
649854ee02bSLaurent Vivier         }
650854ee02bSLaurent Vivier 
651854ee02bSLaurent Vivier         g_ptr_array_add(args, g_strdup("--search"));
652854ee02bSLaurent Vivier         g_ptr_array_add(args, g_string_free(domains, FALSE));
653854ee02bSLaurent Vivier     }
654854ee02bSLaurent Vivier 
655854ee02bSLaurent Vivier     if (passt->has_tcp_ports && passt->tcp_ports) {
656854ee02bSLaurent Vivier         const StringList *list = passt->tcp_ports;
657854ee02bSLaurent Vivier         GString *tcp_ports = g_string_new(list->value->str);
658854ee02bSLaurent Vivier 
659854ee02bSLaurent Vivier         list = list->next;
660854ee02bSLaurent Vivier         while (list) {
661854ee02bSLaurent Vivier             g_string_append(tcp_ports, ",");
662854ee02bSLaurent Vivier             g_string_append(tcp_ports, list->value->str);
663854ee02bSLaurent Vivier             list = list->next;
664854ee02bSLaurent Vivier         }
665854ee02bSLaurent Vivier 
666854ee02bSLaurent Vivier         g_ptr_array_add(args, g_strdup("--tcp-ports"));
667854ee02bSLaurent Vivier         g_ptr_array_add(args, g_string_free(tcp_ports, FALSE));
668854ee02bSLaurent Vivier     }
669854ee02bSLaurent Vivier 
670854ee02bSLaurent Vivier     if (passt->has_udp_ports && passt->udp_ports) {
671854ee02bSLaurent Vivier         const StringList *list = passt->udp_ports;
672854ee02bSLaurent Vivier         GString *udp_ports = g_string_new(list->value->str);
673854ee02bSLaurent Vivier 
674854ee02bSLaurent Vivier         list = list->next;
675854ee02bSLaurent Vivier         while (list) {
676854ee02bSLaurent Vivier             g_string_append(udp_ports, ",");
677854ee02bSLaurent Vivier             g_string_append(udp_ports, list->value->str);
678854ee02bSLaurent Vivier             list = list->next;
679854ee02bSLaurent Vivier         }
680854ee02bSLaurent Vivier 
681854ee02bSLaurent Vivier         g_ptr_array_add(args, g_strdup("--udp-ports"));
682854ee02bSLaurent Vivier         g_ptr_array_add(args, g_string_free(udp_ports, FALSE));
683854ee02bSLaurent Vivier     }
684854ee02bSLaurent Vivier 
685854ee02bSLaurent Vivier     if (passt->has_param && passt->param) {
686854ee02bSLaurent Vivier         const StringList *list = passt->param;
687854ee02bSLaurent Vivier 
688854ee02bSLaurent Vivier         while (list) {
689854ee02bSLaurent Vivier             g_ptr_array_add(args, g_strdup(list->value->str));
690854ee02bSLaurent Vivier             list = list->next;
691854ee02bSLaurent Vivier         }
692854ee02bSLaurent Vivier     }
693854ee02bSLaurent Vivier 
694854ee02bSLaurent Vivier     /* provide a pid file to be able to kil passt on exit */
695854ee02bSLaurent Vivier     g_ptr_array_add(args, g_strdup("--pid"));
696854ee02bSLaurent Vivier     g_ptr_array_add(args, g_strdup(pidfile));
697854ee02bSLaurent Vivier 
698854ee02bSLaurent Vivier     /* g_subprocess_launcher_take_fd() will set the socket on fd 3 */
699854ee02bSLaurent Vivier     g_ptr_array_add(args, g_strdup("--fd"));
700854ee02bSLaurent Vivier     g_ptr_array_add(args, g_strdup("3"));
701854ee02bSLaurent Vivier 
702854ee02bSLaurent Vivier     g_ptr_array_add(args, NULL);
703854ee02bSLaurent Vivier 
704854ee02bSLaurent Vivier     return args;
705854ee02bSLaurent Vivier }
706854ee02bSLaurent Vivier 
707854ee02bSLaurent Vivier int net_init_passt(const Netdev *netdev, const char *name,
708854ee02bSLaurent Vivier                    NetClientState *peer, Error **errp)
709854ee02bSLaurent Vivier {
710854ee02bSLaurent Vivier     g_autoptr(GError) error = NULL;
711854ee02bSLaurent Vivier     NetClientState *nc;
712854ee02bSLaurent Vivier     NetPasstState *s;
713854ee02bSLaurent Vivier     GPtrArray *args;
714854ee02bSLaurent Vivier     gchar *pidfile;
715854ee02bSLaurent Vivier     int pidfd;
716854ee02bSLaurent Vivier 
717854ee02bSLaurent Vivier     assert(netdev->type == NET_CLIENT_DRIVER_PASST);
718854ee02bSLaurent Vivier 
719854ee02bSLaurent Vivier     pidfd = g_file_open_tmp("passt-XXXXXX.pid", &pidfile, &error);
720854ee02bSLaurent Vivier     if (pidfd == -1) {
721854ee02bSLaurent Vivier         error_setg(errp, "Failed to create temporary file: %s", error->message);
722854ee02bSLaurent Vivier         return -1;
723854ee02bSLaurent Vivier     }
724854ee02bSLaurent Vivier     close(pidfd);
725854ee02bSLaurent Vivier 
726854ee02bSLaurent Vivier     args = net_passt_decode_args(&netdev->u.passt, pidfile, errp);
727854ee02bSLaurent Vivier     if (args == NULL) {
728854ee02bSLaurent Vivier         g_free(pidfile);
729854ee02bSLaurent Vivier         return -1;
730854ee02bSLaurent Vivier     }
731854ee02bSLaurent Vivier 
732854ee02bSLaurent Vivier     nc = qemu_new_net_client(&net_passt_info, peer, "passt", name);
733854ee02bSLaurent Vivier     s = DO_UPCAST(NetPasstState, data.nc, nc);
734854ee02bSLaurent Vivier 
735854ee02bSLaurent Vivier     s->args = args;
736854ee02bSLaurent Vivier     s->pidfile = pidfile;
737854ee02bSLaurent Vivier 
738*da703b06SLaurent Vivier     if (netdev->u.passt.has_vhost_user && netdev->u.passt.vhost_user) {
739*da703b06SLaurent Vivier         if (net_passt_vhost_user_init(s, errp) == -1) {
740*da703b06SLaurent Vivier             qemu_del_net_client(nc);
741*da703b06SLaurent Vivier             return -1;
742*da703b06SLaurent Vivier         }
743*da703b06SLaurent Vivier 
744*da703b06SLaurent Vivier         return 0;
745*da703b06SLaurent Vivier     }
746*da703b06SLaurent Vivier 
747854ee02bSLaurent Vivier     if (net_passt_stream_start(s, errp) == -1) {
748854ee02bSLaurent Vivier         qemu_del_net_client(nc);
749854ee02bSLaurent Vivier         return -1;
750854ee02bSLaurent Vivier     }
751854ee02bSLaurent Vivier 
752854ee02bSLaurent Vivier     return 0;
753854ee02bSLaurent Vivier }
754