Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/usr.sbin/bhyve/net_backend_slirp.c
107110 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 2023, 2025 Mark Johnston <[email protected]>
5
*
6
* This software was developed by the University of Cambridge Computer
7
* Laboratory (Department of Computer Science and Technology) under Innovate
8
* UK project 105694, "Digital Security by Design (DSbD) Technology Platform
9
* Prototype".
10
*
11
* Redistribution and use in source and binary forms, with or without
12
* modification, are permitted provided that the following conditions
13
* are met:
14
* 1. Redistributions of source code must retain the above copyright
15
* notice, this list of conditions and the following disclaimer.
16
* 2. Redistributions in binary form must reproduce the above copyright
17
* notice, this list of conditions and the following disclaimer in the
18
* documentation and/or other materials provided with the distribution.
19
*
20
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30
* SUCH DAMAGE.
31
*/
32
33
/*
34
* The slirp backend enables unprivileged userspace networking via libslirp,
35
* which must be installed on the host system via pkg or the ports tree.
36
* libslirp.so is dlopen()ed into a helper process with which this backend
37
* communicates.
38
*
39
* Packets received from the guest (i.e., transmitted by the frontend, such as a
40
* virtio NIC device model) are injected into the slirp backend via slirp_send(),
41
* which sends the packet to the helper process.
42
*
43
* Packets to be transmitted to the guest (i.e., inserted into the frontend's
44
* receive buffers) are buffered in a per-interface socket pair and read by the
45
* mevent loop. Sockets instantiated by libslirp are monitored by a thread
46
* which uses poll() and slirp_pollfds_poll() to drive libslirp events; this
47
* thread also handles timeout events from the libslirp context.
48
*/
49
50
#include <sys/socket.h>
51
#include <sys/wait.h>
52
53
#include <assert.h>
54
#include <errno.h>
55
#include <signal.h>
56
#include <spawn.h>
57
#include <stdio.h>
58
#include <stdlib.h>
59
#include <string.h>
60
#include <unistd.h>
61
62
#include "config.h"
63
#include "debug.h"
64
#include "mevent.h"
65
#include "net_utils.h"
66
#include "net_backends.h"
67
#include "net_backends_priv.h"
68
69
#define DEFAULT_MTU 2048
70
71
struct slirp_priv {
72
int s;
73
pid_t helper;
74
struct mevent *mevp;
75
size_t mtu;
76
uint8_t *buf;
77
};
78
79
extern char **environ;
80
81
static int
82
slirp_init(struct net_backend *be, const char *devname __unused,
83
nvlist_t *nvl, net_be_rxeof_t cb, void *param)
84
{
85
struct slirp_priv *priv = NET_BE_PRIV(be);
86
nvlist_t *config;
87
posix_spawn_file_actions_t fa;
88
pid_t child;
89
const char **argv;
90
char sockname[32];
91
int error, s[2];
92
const char *mtu_value;
93
size_t mtu;
94
95
if (socketpair(PF_LOCAL, SOCK_SEQPACKET | SOCK_NONBLOCK, 0, s) != 0) {
96
EPRINTLN("socketpair");
97
return (-1);
98
}
99
100
/*
101
* The child will exit once its connection goes away, so make sure only
102
* one end is inherited by the child.
103
*/
104
if (posix_spawn_file_actions_init(&fa) != 0) {
105
EPRINTLN("posix_spawn_file_actions_init");
106
goto err;
107
}
108
if (posix_spawn_file_actions_addclose(&fa, s[0]) != 0) {
109
EPRINTLN("posix_spawn_file_actions_addclose");
110
posix_spawn_file_actions_destroy(&fa);
111
goto err;
112
}
113
114
(void)snprintf(sockname, sizeof(sockname), "%d", s[1]);
115
argv = (const char *[]){
116
"/usr/libexec/bhyve-slirp-helper", "-S", sockname, NULL
117
};
118
error = posix_spawn(&child, "/usr/libexec/bhyve-slirp-helper",
119
&fa, NULL, __DECONST(char **, argv), environ);
120
posix_spawn_file_actions_destroy(&fa);
121
if (error != 0) {
122
EPRINTLN("posix_spawn(bhyve-slirp-helper): %s",
123
strerror(error));
124
goto err;
125
}
126
127
config = nvlist_clone(nvl);
128
if (config == NULL) {
129
EPRINTLN("nvlist_clone");
130
goto err;
131
}
132
133
mtu_value = get_config_value_node(config, "mtu");
134
if (mtu_value != NULL) {
135
if (net_parsemtu(mtu_value, &mtu)) {
136
EPRINTLN("Could not parse MTU");
137
goto err;
138
}
139
} else {
140
mtu = DEFAULT_MTU;
141
}
142
nvlist_add_number(config, "mtui", mtu);
143
144
priv->mtu = mtu;
145
priv->buf = malloc(mtu);
146
if (priv->buf == NULL) {
147
EPRINTLN("Could not allocate buffer");
148
goto err;
149
}
150
151
nvlist_add_string(config, "vmname", get_config_value("name"));
152
error = nvlist_send(s[0], config);
153
nvlist_destroy(config);
154
if (error != 0) {
155
EPRINTLN("nvlist_send");
156
goto err;
157
}
158
159
be->fd = s[0];
160
priv->mevp = mevent_add_disabled(be->fd, EVF_READ, cb, param);
161
if (priv->mevp == NULL) {
162
EPRINTLN("Could not register event");
163
goto err;
164
}
165
166
priv->helper = child;
167
priv->s = s[0];
168
(void)close(s[1]);
169
170
return (0);
171
172
err:
173
free(priv->buf);
174
(void)close(s[0]);
175
(void)close(s[1]);
176
return (-1);
177
}
178
179
static ssize_t
180
slirp_send(struct net_backend *be, const struct iovec *iov, int iovcnt)
181
{
182
struct slirp_priv *priv = NET_BE_PRIV(be);
183
struct msghdr hdr;
184
185
memset(&hdr, 0, sizeof(hdr));
186
hdr.msg_iov = __DECONST(struct iovec *, iov);
187
hdr.msg_iovlen = iovcnt;
188
return (sendmsg(priv->s, &hdr, MSG_EOR));
189
}
190
191
static void
192
slirp_cleanup(struct net_backend *be)
193
{
194
struct slirp_priv *priv = NET_BE_PRIV(be);
195
196
free(priv->buf);
197
198
if (priv->helper > 0) {
199
int status;
200
201
if (kill(priv->helper, SIGKILL) != 0) {
202
EPRINTLN("kill(bhyve-slirp-helper): %s",
203
strerror(errno));
204
return;
205
}
206
(void)waitpid(priv->helper, &status, 0);
207
}
208
}
209
210
static ssize_t
211
slirp_peek_recvlen(struct net_backend *be)
212
{
213
struct slirp_priv *priv = NET_BE_PRIV(be);
214
ssize_t n;
215
216
/*
217
* Copying into the buffer is totally unnecessary, but we don't
218
* implement MSG_TRUNC for SEQPACKET sockets.
219
*/
220
n = recv(priv->s, priv->buf, priv->mtu, MSG_PEEK | MSG_DONTWAIT);
221
if (n < 0)
222
return (errno == EWOULDBLOCK ? 0 : -1);
223
return (n);
224
}
225
226
static ssize_t
227
slirp_recv(struct net_backend *be, const struct iovec *iov, int iovcnt)
228
{
229
struct slirp_priv *priv = NET_BE_PRIV(be);
230
struct msghdr hdr;
231
ssize_t n;
232
233
hdr.msg_name = NULL;
234
hdr.msg_namelen = 0;
235
hdr.msg_iov = __DECONST(struct iovec *, iov);
236
hdr.msg_iovlen = iovcnt;
237
hdr.msg_control = NULL;
238
hdr.msg_controllen = 0;
239
hdr.msg_flags = 0;
240
n = recvmsg(priv->s, &hdr, MSG_DONTWAIT);
241
if (n < 0) {
242
if (errno == EWOULDBLOCK)
243
return (0);
244
return (-1);
245
}
246
assert((size_t)n <= priv->mtu);
247
return (n);
248
}
249
250
static void
251
slirp_recv_enable(struct net_backend *be)
252
{
253
struct slirp_priv *priv = NET_BE_PRIV(be);
254
255
mevent_enable(priv->mevp);
256
}
257
258
static void
259
slirp_recv_disable(struct net_backend *be)
260
{
261
struct slirp_priv *priv = NET_BE_PRIV(be);
262
263
mevent_disable(priv->mevp);
264
}
265
266
static uint64_t
267
slirp_get_cap(struct net_backend *be __unused)
268
{
269
return (0);
270
}
271
272
static int
273
slirp_set_cap(struct net_backend *be __unused, uint64_t features __unused,
274
unsigned int vnet_hdr_len __unused)
275
{
276
return ((features || vnet_hdr_len) ? -1 : 0);
277
}
278
279
static struct net_backend slirp_backend = {
280
.prefix = "slirp",
281
.priv_size = sizeof(struct slirp_priv),
282
.init = slirp_init,
283
.cleanup = slirp_cleanup,
284
.send = slirp_send,
285
.peek_recvlen = slirp_peek_recvlen,
286
.recv = slirp_recv,
287
.recv_enable = slirp_recv_enable,
288
.recv_disable = slirp_recv_disable,
289
.get_cap = slirp_get_cap,
290
.set_cap = slirp_set_cap,
291
};
292
293
DATA_SET(net_backend_set, slirp_backend);
294
295