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