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
checked_close(int * fdp)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
slirp_init_once(void)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
slirp_priv_init(struct slirp_priv * priv)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
slirp_priv_cleanup(struct slirp_priv * priv)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
slirp_cb_clock_get_ns(void * param __unused)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
slirp_cb_notify(void * param)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
slirp_cb_register_poll_fd(int fd,void * param __unused)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
slirp_cb_send_packet(const void * buf,size_t len,void * param)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
slirp_cb_unregister_poll_fd(int fd __unused,void * opaque __unused)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
slirpev2pollev(int events)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
pollev2slirpev(int events)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
slirp_addpoll_cb(int fd,int events,void * param)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
slirp_poll_revents(int idx,void * param)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 *
slirp_pollfd_td_loop(void * param)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
parse_addr(char * addr,struct sockaddr_in * sinp)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
parse_hostfwd_rule(const char * descr,int * is_udp,struct sockaddr * hostaddr,struct sockaddr * guestaddr)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
config_one_hostfwd(struct slirp_priv * priv,const char * rule)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
_slirp_init(struct net_backend * be,const char * devname __unused,nvlist_t * nvl,net_be_rxeof_t cb,void * param)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
slirp_send(struct net_backend * be,const struct iovec * iov,int iovcnt)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
_slirp_cleanup(struct net_backend * be)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
slirp_peek_recvlen(struct net_backend * be)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
slirp_recv(struct net_backend * be,const struct iovec * iov,int iovcnt)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
slirp_recv_enable(struct net_backend * be)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
slirp_recv_disable(struct net_backend * be)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
slirp_get_cap(struct net_backend * be __unused)695 slirp_get_cap(struct net_backend *be __unused)
696 {
697 return (0);
698 }
699
700 static int
slirp_set_cap(struct net_backend * be __unused,uint64_t features __unused,unsigned int vnet_hdr_len __unused)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