xref: /freebsd/usr.sbin/bhyve/net_backend_slirp.c (revision dd21556857e8d40f66bf5ad54754d9d52669ebf7)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2023 Mark Johnston <markj@FreeBSD.org>
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 networking via libslirp, which must be
35  * installed on the host system via pkg or the ports tree.  bhyve dlopen()s
36  * libslirp.so upon instantiating the slirp backend.  Various network parameters
37  * are hard-coded in _slirp_init().
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  * Packets to be transmitted to the guest (i.e., inserted into the frontend's
42  * receive buffers) are buffered in a per-interface socket pair and read by the
43  * mevent loop.  Sockets instantiated by libslirp are monitored by a thread
44  * which uses poll() and slirp_pollfds_poll() to drive libslirp events; this
45  * thread also handles timeout events from the libslirp context.
46  */
47 
48 #include <sys/socket.h>
49 
50 #include <assert.h>
51 #include <capsicum_helpers.h>
52 #include <dlfcn.h>
53 #include <errno.h>
54 #include <poll.h>
55 #include <pthread.h>
56 #include <pthread_np.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 "libslirp.h"
65 #include "mevent.h"
66 #include "net_backends.h"
67 #include "net_backends_priv.h"
68 
69 typedef int (*slirp_add_hostxfwd_p_t)(Slirp *,
70     const struct sockaddr *, socklen_t, const struct sockaddr *, socklen_t,
71     int);
72 typedef void (*slirp_cleanup_p_t)(Slirp *);
73 typedef void (*slirp_input_p_t)(Slirp *, const uint8_t *, int);
74 typedef Slirp *(*slirp_new_p_t)(const SlirpConfig *, const SlirpCb *, void *);
75 typedef void (*slirp_pollfds_fill_p_t)(Slirp *, uint32_t *timeout,
76     SlirpAddPollCb, void *);
77 typedef void (*slirp_pollfds_poll_p_t)(Slirp *, int, SlirpGetREventsCb, void *);
78 
79 /* Function pointer table, initialized by slirp_init_once(). */
80 static slirp_add_hostxfwd_p_t slirp_add_hostxfwd_p;
81 static slirp_cleanup_p_t slirp_cleanup_p;
82 static slirp_input_p_t slirp_input_p;
83 static slirp_new_p_t slirp_new_p;
84 static slirp_pollfds_fill_p_t slirp_pollfds_fill_p;
85 static slirp_pollfds_poll_p_t slirp_pollfds_poll_p;
86 
87 static void
88 checked_close(int *fdp)
89 {
90 	int error;
91 
92 	if (*fdp != -1) {
93 		error = close(*fdp);
94 		assert(error == 0);
95 		*fdp = -1;
96 	}
97 }
98 
99 static int
100 slirp_init_once(void)
101 {
102 	static void *handle = NULL;
103 
104 	if (handle != NULL)
105 		return (0);
106 	handle = dlopen("libslirp.so.0", RTLD_LAZY);
107 	if (handle == NULL) {
108 		EPRINTLN("Unable to open libslirp.so.0: %s", dlerror());
109 		return (-1);
110 	}
111 
112 #define IMPORT_SYM(sym) do {					\
113 	sym##_p = (sym##_p_t)dlsym(handle, #sym);		\
114 	if (sym##_p == NULL) {					\
115 		EPRINTLN("failed to resolve %s", #sym);		\
116 		goto err;					\
117 	}							\
118 } while (0)
119 	IMPORT_SYM(slirp_add_hostxfwd);
120 	IMPORT_SYM(slirp_cleanup);
121 	IMPORT_SYM(slirp_input);
122 	IMPORT_SYM(slirp_new);
123 	IMPORT_SYM(slirp_pollfds_fill);
124 	IMPORT_SYM(slirp_pollfds_poll);
125 #undef IMPORT_SYM
126 
127 	/*
128 	 * libslirp uses glib, which uses tzdata to format log messages.  Help
129 	 * it out.
130 	 *
131 	 * XXX-MJ glib will also look for charset files, not sure what we can do
132 	 * about that...
133 	 */
134 	caph_cache_tzdata();
135 
136 	return (0);
137 
138 err:
139 	dlclose(handle);
140 	handle = NULL;
141 	return (-1);
142 }
143 
144 struct slirp_priv {
145 	Slirp *slirp;
146 
147 #define	SLIRP_MTU	2048
148 	struct mevent *mevp;
149 	int pipe[2];		/* used to buffer data sent to the guest */
150 	int wakeup[2];		/* used to wake up the pollfd thread */
151 
152 	pthread_t pollfd_td;
153 	struct pollfd *pollfds;
154 	size_t npollfds;
155 
156 	/* Serializes libslirp calls. */
157 	pthread_mutex_t mtx;
158 };
159 
160 static void
161 slirp_priv_init(struct slirp_priv *priv)
162 {
163 	int error;
164 
165 	memset(priv, 0, sizeof(*priv));
166 	priv->pipe[0] = priv->pipe[1] = -1;
167 	priv->wakeup[0] = priv->wakeup[1] = -1;
168 	error = pthread_mutex_init(&priv->mtx, NULL);
169 	assert(error == 0);
170 }
171 
172 static void
173 slirp_priv_cleanup(struct slirp_priv *priv)
174 {
175 	int error;
176 
177 	checked_close(&priv->pipe[0]);
178 	checked_close(&priv->pipe[1]);
179 	checked_close(&priv->wakeup[0]);
180 	checked_close(&priv->wakeup[1]);
181 	if (priv->mevp)
182 		mevent_delete(priv->mevp);
183 	if (priv->slirp != NULL)
184 		slirp_cleanup_p(priv->slirp);
185 	error = pthread_mutex_destroy(&priv->mtx);
186 	assert(error == 0);
187 }
188 
189 static int64_t
190 slirp_cb_clock_get_ns(void *param __unused)
191 {
192 	struct timespec ts;
193 	int error;
194 
195 	error = clock_gettime(CLOCK_MONOTONIC, &ts);
196 	assert(error == 0);
197 	return ((int64_t)(ts.tv_sec * 1000000000L + ts.tv_nsec));
198 }
199 
200 static void
201 slirp_cb_notify(void *param)
202 {
203 	struct slirp_priv *priv;
204 
205 	/* Wake up the poll thread.  We assume that priv->mtx is held here. */
206 	priv = param;
207 	(void)write(priv->wakeup[1], "M", 1);
208 }
209 
210 static void
211 slirp_cb_register_poll_fd(int fd, void *param __unused)
212 {
213 	const int one = 1;
214 
215 	(void)setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &one, sizeof(int));
216 }
217 
218 static ssize_t
219 slirp_cb_send_packet(const void *buf, size_t len, void *param)
220 {
221 	struct slirp_priv *priv;
222 	ssize_t n;
223 
224 	priv = param;
225 
226 	assert(len <= SLIRP_MTU);
227 	n = send(priv->pipe[1], buf, len, 0);
228 	if (n < 0) {
229 		EPRINTLN("slirp_cb_send_packet: send: %s", strerror(errno));
230 		return (n);
231 	}
232 	assert((size_t)n == len);
233 
234 	return (n);
235 }
236 
237 static void
238 slirp_cb_unregister_poll_fd(int fd __unused, void *opaque __unused)
239 {
240 }
241 
242 /* Callbacks invoked from within libslirp. */
243 static const struct SlirpCb slirp_cbs = {
244 	.clock_get_ns = slirp_cb_clock_get_ns,
245 	.notify = slirp_cb_notify,
246 	.register_poll_fd = slirp_cb_register_poll_fd,
247 	.send_packet = slirp_cb_send_packet,
248 	.unregister_poll_fd = slirp_cb_unregister_poll_fd,
249 };
250 
251 static int
252 slirpev2pollev(int events)
253 {
254 	int ret;
255 
256 	ret = 0;
257 	if (events & SLIRP_POLL_IN)
258 		ret |= POLLIN;
259 	if (events & SLIRP_POLL_OUT)
260 		ret |= POLLOUT;
261 	if (events & SLIRP_POLL_PRI)
262 		ret |= POLLPRI;
263 	if (events & SLIRP_POLL_ERR)
264 		ret |= POLLERR;
265 	if (events & SLIRP_POLL_HUP)
266 		ret |= POLLHUP;
267 	return (ret);
268 }
269 
270 static int
271 pollev2slirpev(int events)
272 {
273 	int ret;
274 
275 	ret = 0;
276 	if (events & POLLIN)
277 		ret |= SLIRP_POLL_IN;
278 	if (events & POLLOUT)
279 		ret |= SLIRP_POLL_OUT;
280 	if (events & POLLPRI)
281 		ret |= SLIRP_POLL_PRI;
282 	if (events & POLLERR)
283 		ret |= SLIRP_POLL_ERR;
284 	if (events & POLLHUP)
285 		ret |= SLIRP_POLL_HUP;
286 	return (ret);
287 }
288 
289 static int
290 slirp_addpoll_cb(int fd, int events, void *param)
291 {
292 	struct slirp_priv *priv;
293 	struct pollfd *pollfd, *pollfds;
294 	size_t i;
295 
296 	priv = param;
297 
298 	for (i = 0; i < priv->npollfds; i++)
299 		if (priv->pollfds[i].fd == -1)
300 			break;
301 	if (i == priv->npollfds) {
302 		const size_t POLLFD_GROW = 4;
303 
304 		priv->npollfds += POLLFD_GROW;
305 		pollfds = realloc(priv->pollfds,
306 		    sizeof(*pollfds) * priv->npollfds);
307 		if (pollfds == NULL)
308 			return (-1);
309 		for (i = priv->npollfds - POLLFD_GROW; i < priv->npollfds; i++)
310 			pollfds[i].fd = -1;
311 		priv->pollfds = pollfds;
312 
313 		i = priv->npollfds - POLLFD_GROW;
314 	}
315 	pollfd = &priv->pollfds[i];
316 	pollfd->fd = fd;
317 	pollfd->events = slirpev2pollev(events);
318 	pollfd->revents = 0;
319 
320 	return ((int)i);
321 }
322 
323 static int
324 slirp_poll_revents(int idx, void *param)
325 {
326 	struct slirp_priv *priv;
327 	struct pollfd *pollfd;
328 	short revents;
329 
330 	priv = param;
331 	assert(idx >= 0);
332 	assert((unsigned int)idx < priv->npollfds);
333 	pollfd = &priv->pollfds[idx];
334 	assert(pollfd->fd != -1);
335 
336 	/* The kernel may report POLLHUP even if we didn't ask for it. */
337 	revents = pollfd->revents;
338 	if ((pollfd->events & POLLHUP) == 0)
339 		revents &= ~POLLHUP;
340 	return (pollev2slirpev(revents));
341 }
342 
343 static void *
344 slirp_pollfd_td_loop(void *param)
345 {
346 	struct slirp_priv *priv;
347 	struct pollfd *pollfds;
348 	size_t npollfds;
349 	uint32_t timeout;
350 	int error;
351 
352 	pthread_set_name_np(pthread_self(), "slirp pollfd");
353 	priv = param;
354 
355 	pthread_mutex_lock(&priv->mtx);
356 	for (;;) {
357 		int wakeup;
358 
359 		for (size_t i = 0; i < priv->npollfds; i++)
360 			priv->pollfds[i].fd = -1;
361 
362 		/* Register for notifications from slirp_cb_notify(). */
363 		wakeup = slirp_addpoll_cb(priv->wakeup[0], POLLIN, priv);
364 
365 		timeout = UINT32_MAX;
366 		slirp_pollfds_fill_p(priv->slirp, &timeout, slirp_addpoll_cb,
367 		    priv);
368 
369 		pollfds = priv->pollfds;
370 		npollfds = priv->npollfds;
371 		pthread_mutex_unlock(&priv->mtx);
372 		error = poll(pollfds, npollfds, timeout);
373 		if (error == -1 && errno != EINTR) {
374 			EPRINTLN("poll: %s", strerror(errno));
375 			exit(1);
376 		}
377 		pthread_mutex_lock(&priv->mtx);
378 		slirp_pollfds_poll_p(priv->slirp, error == -1,
379 		    slirp_poll_revents, priv);
380 
381 		/*
382 		 * If we were woken up by the notify callback, mask the
383 		 * interrupt.
384 		 */
385 		if ((pollfds[wakeup].revents & POLLIN) != 0) {
386 			ssize_t n;
387 
388 			do {
389 				uint8_t b;
390 
391 				n = read(priv->wakeup[0], &b, 1);
392 			} while (n == 1);
393 			if (n != -1 || errno != EAGAIN) {
394 				EPRINTLN("read(wakeup): %s", strerror(errno));
395 				exit(1);
396 			}
397 		}
398 	}
399 }
400 
401 static int
402 parse_addr(char *addr, struct sockaddr_in *sinp)
403 {
404 	char *port;
405 	int error, porti;
406 
407 	memset(sinp, 0, sizeof(*sinp));
408 	sinp->sin_family = AF_INET;
409 	sinp->sin_len = sizeof(struct sockaddr_in);
410 
411 	port = strchr(addr, ':');
412 	if (port == NULL)
413 		return (EINVAL);
414 	*port++ = '\0';
415 
416 	if (strlen(addr) > 0) {
417 		error = inet_pton(AF_INET, addr, &sinp->sin_addr);
418 		if (error != 1)
419 			return (error == 0 ? EPFNOSUPPORT : errno);
420 	} else {
421 		sinp->sin_addr.s_addr = htonl(INADDR_ANY);
422 	}
423 
424 	porti = strlen(port) > 0 ? atoi(port) : 0;
425 	if (porti < 0 || porti > UINT16_MAX)
426 		return (EINVAL);
427 	sinp->sin_port = htons(porti);
428 
429 	return (0);
430 }
431 
432 static int
433 parse_hostfwd_rule(const char *descr, int *is_udp, struct sockaddr *hostaddr,
434     struct sockaddr *guestaddr)
435 {
436 	struct sockaddr_in *hostaddrp, *guestaddrp;
437 	const char *proto;
438 	char *p, *host, *guest;
439 	int error;
440 
441 	error = 0;
442 	*is_udp = 0;
443 
444 	p = strdup(descr);
445 	if (p == NULL)
446 		return (ENOMEM);
447 
448 	host = strchr(p, ':');
449 	if (host == NULL) {
450 		error = EINVAL;
451 		goto out;
452 	}
453 	*host++ = '\0';
454 
455 	proto = p;
456 	*is_udp = strcmp(proto, "udp") == 0;
457 
458 	guest = strchr(host, '-');
459 	if (guest == NULL) {
460 		error = EINVAL;
461 		goto out;
462 	}
463 	*guest++ = '\0';
464 
465 	hostaddrp = (struct sockaddr_in *)hostaddr;
466 	error = parse_addr(host, hostaddrp);
467 	if (error != 0)
468 		goto out;
469 
470 	guestaddrp = (struct sockaddr_in *)guestaddr;
471 	error = parse_addr(guest, guestaddrp);
472 	if (error != 0)
473 		goto out;
474 
475 out:
476 	free(p);
477 	return (error);
478 }
479 
480 static int
481 config_one_hostfwd(struct slirp_priv *priv, const char *rule)
482 {
483 	struct sockaddr hostaddr, guestaddr;
484 	int error, is_udp;
485 
486 	error = parse_hostfwd_rule(rule, &is_udp, &hostaddr, &guestaddr);
487 	if (error != 0) {
488 		EPRINTLN("Unable to parse hostfwd rule '%s': %s",
489 		    rule, strerror(error));
490 		return (error);
491 	}
492 
493 	error = slirp_add_hostxfwd_p(priv->slirp, &hostaddr, hostaddr.sa_len,
494 	    &guestaddr, guestaddr.sa_len, is_udp ? SLIRP_HOSTFWD_UDP : 0);
495 	if (error != 0) {
496 		EPRINTLN("Unable to add hostfwd rule '%s': %s",
497 		    rule, strerror(errno));
498 		return (error);
499 	}
500 
501 	return (0);
502 }
503 
504 static int
505 _slirp_init(struct net_backend *be, const char *devname __unused,
506     nvlist_t *nvl, net_be_rxeof_t cb, void *param)
507 {
508 	struct slirp_priv *priv = NET_BE_PRIV(be);
509 	SlirpConfig config = {
510 		.version = 4,
511 		.if_mtu = SLIRP_MTU,
512 		.restricted = true,
513 		.in_enabled = true,
514 		.vnetwork.s_addr = htonl(0x0a000200),	/* 10.0.2.0/24 */
515 		.vnetmask.s_addr = htonl(0xffffff00),
516 		.vdhcp_start.s_addr = htonl(0x0a00020f),/* 10.0.2.15 */
517 		.vhost.s_addr = htonl(0x0a000202),	/* 10.0.2.2 */
518 		.enable_emu = false,
519 	};
520 	const char *hostfwd;
521 	int error, sndbuf;
522 
523 	error = slirp_init_once();
524 	if (error != 0)
525 		return (error);
526 
527 	slirp_priv_init(priv);
528 
529 	priv->slirp = slirp_new_p(&config, &slirp_cbs, priv);
530 	if (priv->slirp == NULL) {
531 		EPRINTLN("Unable to create slirp instance");
532 		goto err;
533 	}
534 
535 	hostfwd = get_config_value_node(nvl, "hostfwd");
536 	if (hostfwd != NULL) {
537 		char *rules, *tofree;
538 		const char *rule;
539 
540 		tofree = rules = strdup(hostfwd);
541 		if (rules == NULL)
542 			goto err;
543 		while ((rule = strsep(&rules, ";")) != NULL) {
544 			error = config_one_hostfwd(priv, rule);
545 			if (error != 0) {
546 				free(tofree);
547 				goto err;
548 			}
549 		}
550 		free(tofree);
551 	}
552 
553 	error = socketpair(PF_LOCAL, SOCK_DGRAM | SOCK_CLOEXEC, 0, priv->pipe);
554 	if (error != 0) {
555 		EPRINTLN("Unable to create pipe: %s", strerror(errno));
556 		goto err;
557 	}
558 
559 	error = pipe2(priv->wakeup, O_CLOEXEC | O_NONBLOCK);
560 	if (error != 0) {
561 		EPRINTLN("Unable to create wakeup pipe: %s", strerror(errno));
562 		goto err;
563 	}
564 
565 	/*
566 	 * Try to avoid dropping buffered packets in slirp_cb_send_packet().
567 	 */
568 	sndbuf = 1024 * 1024;
569 	error = setsockopt(priv->pipe[1], SOL_SOCKET, SO_SNDBUF, &sndbuf,
570 	    sizeof(sndbuf));
571 	if (error != 0) {
572 		EPRINTLN("Could not set socket buffer size: %s",
573 		    strerror(errno));
574 		goto err;
575 	}
576 
577 	be->fd = priv->pipe[0];
578 	priv->mevp = mevent_add_disabled(be->fd, EVF_READ, cb, param);
579 	if (priv->mevp == NULL) {
580 		EPRINTLN("Could not register event");
581 		goto err;
582 	}
583 
584 	error = pthread_create(&priv->pollfd_td, NULL, slirp_pollfd_td_loop,
585 	    priv);
586 	if (error != 0) {
587 		EPRINTLN("Unable to create pollfd thread: %s", strerror(error));
588 		goto err;
589 	}
590 
591 	return (0);
592 
593 err:
594 	slirp_priv_cleanup(priv);
595 	return (-1);
596 }
597 
598 static ssize_t
599 slirp_send(struct net_backend *be, const struct iovec *iov, int iovcnt)
600 {
601 	struct slirp_priv *priv = NET_BE_PRIV(be);
602 
603 	if (iovcnt == 1) {
604 		/* We can avoid copying if there's a single segment. */
605 		pthread_mutex_lock(&priv->mtx);
606 		slirp_input_p(priv->slirp, iov->iov_base,
607 		    (int)iov->iov_len);
608 		pthread_mutex_unlock(&priv->mtx);
609 		return (iov[0].iov_len);
610 	} else {
611 		uint8_t *pkt;
612 		size_t pktlen;
613 
614 		pktlen = 0;
615 		for (int i = 0; i < iovcnt; i++)
616 			pktlen += iov[i].iov_len;
617 		pkt = malloc(pktlen);
618 		if (pkt == NULL)
619 			return (-1);
620 		pktlen = 0;
621 		for (int i = 0; i < iovcnt; i++) {
622 			memcpy(pkt + pktlen, iov[i].iov_base, iov[i].iov_len);
623 			pktlen += iov[i].iov_len;
624 		}
625 		pthread_mutex_lock(&priv->mtx);
626 		slirp_input_p(priv->slirp, pkt, (int)pktlen);
627 		pthread_mutex_unlock(&priv->mtx);
628 		free(pkt);
629 		return (pktlen);
630 	}
631 }
632 
633 static void
634 _slirp_cleanup(struct net_backend *be)
635 {
636 	struct slirp_priv *priv = NET_BE_PRIV(be);
637 
638 	slirp_priv_cleanup(priv);
639 }
640 
641 static ssize_t
642 slirp_peek_recvlen(struct net_backend *be)
643 {
644 	struct slirp_priv *priv = NET_BE_PRIV(be);
645 	ssize_t n;
646 
647 	n = recv(priv->pipe[0], NULL, 0, MSG_PEEK | MSG_DONTWAIT | MSG_TRUNC);
648 	if (n < 0)
649 		return (errno == EWOULDBLOCK ? 0 : -1);
650 	assert((size_t)n <= SLIRP_MTU);
651 	return (n);
652 }
653 
654 static ssize_t
655 slirp_recv(struct net_backend *be, const struct iovec *iov, int iovcnt)
656 {
657 	struct slirp_priv *priv = NET_BE_PRIV(be);
658 	struct msghdr hdr;
659 	ssize_t n;
660 
661 	hdr.msg_name = NULL;
662 	hdr.msg_namelen = 0;
663 	hdr.msg_iov = __DECONST(struct iovec *, iov);
664 	hdr.msg_iovlen = iovcnt;
665 	hdr.msg_control = NULL;
666 	hdr.msg_controllen = 0;
667 	hdr.msg_flags = 0;
668 	n = recvmsg(priv->pipe[0], &hdr, MSG_DONTWAIT);
669 	if (n < 0) {
670 		if (errno == EWOULDBLOCK)
671 			return (0);
672 		return (-1);
673 	}
674 	assert(n <= SLIRP_MTU);
675 	return (n);
676 }
677 
678 static void
679 slirp_recv_enable(struct net_backend *be)
680 {
681 	struct slirp_priv *priv = NET_BE_PRIV(be);
682 
683 	mevent_enable(priv->mevp);
684 }
685 
686 static void
687 slirp_recv_disable(struct net_backend *be)
688 {
689 	struct slirp_priv *priv = NET_BE_PRIV(be);
690 
691 	mevent_disable(priv->mevp);
692 }
693 
694 static uint64_t
695 slirp_get_cap(struct net_backend *be __unused)
696 {
697 	return (0);
698 }
699 
700 static int
701 slirp_set_cap(struct net_backend *be __unused, uint64_t features __unused,
702     unsigned int vnet_hdr_len __unused)
703 {
704 	return ((features || vnet_hdr_len) ? -1 : 0);
705 }
706 
707 static struct net_backend slirp_backend = {
708 	.prefix = "slirp",
709 	.priv_size = sizeof(struct slirp_priv),
710 	.init = _slirp_init,
711 	.cleanup = _slirp_cleanup,
712 	.send = slirp_send,
713 	.peek_recvlen = slirp_peek_recvlen,
714 	.recv = slirp_recv,
715 	.recv_enable = slirp_recv_enable,
716 	.recv_disable = slirp_recv_disable,
717 	.get_cap = slirp_get_cap,
718 	.set_cap = slirp_set_cap,
719 };
720 
721 DATA_SET(net_backend_set, slirp_backend);
722