mirror of
https://github.com/freebsd/freebsd-src
synced 2024-09-06 17:18:32 +00:00
bhyve: Add a slirp network backend
This enables a subset of the functionality provided by QEMU's user networking implementation. In particular, it uses net/libslirp, the same library as QEMU. libslirp is permissively licensed but has some dependencies which make it impractical to bring into the base system (glib in particular). I thus opted to make bhyve dlopen the libslirp.so, which can be installed via pkg. The library header is imported into bhyve. The slirp backend takes a "hostfwd" which is identical to QEMU's hostfwd. When configured, bhyve opens a host socket and listens for connections, which get forwarded to the guest. For instance, "hostfwd=tcp::1234-:22" allows one to ssh into the guest by ssh'ing to port 1234 on the host, e.g., via 127.0.0.1. I didn't try to hook up guestfwd support since I don't personally have a use-case for it yet, and I think it won't interact nicely with the capsicum sandbox. Reviewed by: jhb Tested by: rew MFC after: 1 month Sponsored by: Innovate UK Differential Revision: https://reviews.freebsd.org/D42510
This commit is contained in:
parent
be74aede49
commit
c5359e2af5
|
@ -34,6 +34,7 @@ SRCS= \
|
|||
mem.c \
|
||||
mevent.c \
|
||||
net_backend_netmap.c \
|
||||
net_backend_slirp.c \
|
||||
net_backends.c \
|
||||
net_utils.c \
|
||||
pci_emul.c \
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
.\" SUCH DAMAGE.
|
||||
.\"
|
||||
.Dd October 12, 2023
|
||||
.Dd November 20, 2023
|
||||
.Dt BHYVE 8
|
||||
.Os
|
||||
.Sh NAME
|
||||
|
@ -426,6 +426,10 @@ Network device backends:
|
|||
.Op Cm \&,mac= Ar xx:xx:xx:xx:xx:xx
|
||||
.Op Cm \&,mtu= Ar N
|
||||
.Xc
|
||||
.It
|
||||
.Xo
|
||||
.Cm slirp,hostfwd= Ar proto : Ar hostaddr : Ar hostport - Ar guestaddr : Ar guestport
|
||||
.Xc
|
||||
.El
|
||||
.Sm on
|
||||
.Pp
|
||||
|
@ -469,6 +473,20 @@ must comply with
|
|||
.Xr netgraph 4
|
||||
addressing rules.
|
||||
.Pp
|
||||
The slirp backend can be used to provide a NATed network to the guest.
|
||||
This backend has poor performance but does not require any network
|
||||
configuration on the host system.
|
||||
It depends on the
|
||||
.Pa net/libslirp
|
||||
port.
|
||||
The
|
||||
.Cm hostfwd
|
||||
option takes a 5-tuple describing how connections from the host are to be
|
||||
forwarded to the guest.
|
||||
Multiple rules can be specified, separated by semicolons.
|
||||
Note that semicolons must be escaped or quoted to prevent the shell from
|
||||
interpreting them.
|
||||
.Pp
|
||||
Block storage device backends:
|
||||
.Sm off
|
||||
.Bl -bullet
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
.\" SUCH DAMAGE.
|
||||
.\"
|
||||
.Dd August 19, 2022
|
||||
.Dd November 20, 2023
|
||||
.Dt BHYVE_CONFIG 5
|
||||
.Os
|
||||
.Sh NAME
|
||||
|
@ -401,6 +401,26 @@ The value of
|
|||
is passed to
|
||||
.Xr nm_open
|
||||
to connect to a netmap port.
|
||||
.It slirp
|
||||
Use the slirp backend to provide a userspace network stack.
|
||||
The
|
||||
.Va hostfwd
|
||||
variable is used to configure how packets from the host are translated
|
||||
before being sent to the guest.
|
||||
.Bl -column "peerhook" "Format" "Default"
|
||||
.It Sy Name Ta Sy Format Ta Sy Default Ta Sy Description
|
||||
.It Va hostfwd Ta string Ta Ta
|
||||
A semicolon-separated list of host forwarding rules, each of the form
|
||||
.Ar proto:haddr:hport-gaddr:gport ,
|
||||
where
|
||||
.Ar proto
|
||||
is either
|
||||
.Ql tcp
|
||||
or
|
||||
.Ql udp .
|
||||
If the guest address is equal to the empty string, packets will be
|
||||
forwarded to the first DHCP-assigned address in the guest.
|
||||
.El
|
||||
.El
|
||||
.Pp
|
||||
If
|
||||
|
|
365
usr.sbin/bhyve/libslirp.h
Normal file
365
usr.sbin/bhyve/libslirp.h
Normal file
|
@ -0,0 +1,365 @@
|
|||
/*-
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* Copyright (c) 1995,1996 Danny Gasparovski. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
* DANNY GASPAROVSKI OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef LIBSLIRP_H
|
||||
#define LIBSLIRP_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <in6addr.h>
|
||||
#include <basetsd.h>
|
||||
typedef SSIZE_T slirp_ssize_t;
|
||||
#ifdef BUILDING_LIBSLIRP
|
||||
# define SLIRP_EXPORT __declspec(dllexport)
|
||||
#else
|
||||
# define SLIRP_EXPORT __declspec(dllimport)
|
||||
#endif
|
||||
#else
|
||||
#include <sys/types.h>
|
||||
typedef ssize_t slirp_ssize_t;
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#define SLIRP_EXPORT
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Opaque structure containing the slirp state */
|
||||
typedef struct Slirp Slirp;
|
||||
|
||||
/* Flags passed to SlirpAddPollCb and to be returned by SlirpGetREventsCb. */
|
||||
enum {
|
||||
SLIRP_POLL_IN = 1 << 0,
|
||||
SLIRP_POLL_OUT = 1 << 1,
|
||||
SLIRP_POLL_PRI = 1 << 2,
|
||||
SLIRP_POLL_ERR = 1 << 3,
|
||||
SLIRP_POLL_HUP = 1 << 4,
|
||||
};
|
||||
|
||||
/* Callback for application to get data from the guest */
|
||||
typedef slirp_ssize_t (*SlirpReadCb)(void *buf, size_t len, void *opaque);
|
||||
/* Callback for application to send data to the guest */
|
||||
typedef slirp_ssize_t (*SlirpWriteCb)(const void *buf, size_t len, void *opaque);
|
||||
/* Timer callback */
|
||||
typedef void (*SlirpTimerCb)(void *opaque);
|
||||
/* Callback for libslirp to register polling callbacks */
|
||||
typedef int (*SlirpAddPollCb)(int fd, int events, void *opaque);
|
||||
/* Callback for libslirp to get polling result */
|
||||
typedef int (*SlirpGetREventsCb)(int idx, void *opaque);
|
||||
|
||||
/* For now libslirp creates only a timer for the IPv6 RA */
|
||||
typedef enum SlirpTimerId {
|
||||
SLIRP_TIMER_RA,
|
||||
SLIRP_TIMER_NUM,
|
||||
} SlirpTimerId;
|
||||
|
||||
/*
|
||||
* Callbacks from slirp, to be set by the application.
|
||||
*
|
||||
* The opaque parameter is set to the opaque pointer given in the slirp_new /
|
||||
* slirp_init call.
|
||||
*/
|
||||
typedef struct SlirpCb {
|
||||
/*
|
||||
* Send an ethernet frame to the guest network. The opaque parameter is the
|
||||
* one given to slirp_init(). If the guest is not ready to receive a frame,
|
||||
* the function can just drop the data. TCP will then handle retransmissions
|
||||
* at a lower pace.
|
||||
* <0 reports an IO error.
|
||||
*/
|
||||
SlirpWriteCb send_packet;
|
||||
/* Print a message for an error due to guest misbehavior. */
|
||||
void (*guest_error)(const char *msg, void *opaque);
|
||||
/* Return the virtual clock value in nanoseconds */
|
||||
int64_t (*clock_get_ns)(void *opaque);
|
||||
/* Create a new timer with the given callback and opaque data. Not
|
||||
* needed if timer_new_opaque is provided. */
|
||||
void *(*timer_new)(SlirpTimerCb cb, void *cb_opaque, void *opaque);
|
||||
/* Remove and free a timer */
|
||||
void (*timer_free)(void *timer, void *opaque);
|
||||
/* Modify a timer to expire at @expire_time (ms) */
|
||||
void (*timer_mod)(void *timer, int64_t expire_time, void *opaque);
|
||||
/* Register a fd for future polling */
|
||||
void (*register_poll_fd)(int fd, void *opaque);
|
||||
/* Unregister a fd */
|
||||
void (*unregister_poll_fd)(int fd, void *opaque);
|
||||
/* Kick the io-thread, to signal that new events may be processed because some TCP buffer
|
||||
* can now receive more data, i.e. slirp_socket_can_recv will return 1. */
|
||||
void (*notify)(void *opaque);
|
||||
|
||||
/*
|
||||
* Fields introduced in SlirpConfig version 4 begin
|
||||
*/
|
||||
|
||||
/* Initialization has completed and a Slirp* has been created. */
|
||||
void (*init_completed)(Slirp *slirp, void *opaque);
|
||||
/* Create a new timer. When the timer fires, the application passes
|
||||
* the SlirpTimerId and cb_opaque to slirp_handle_timer. */
|
||||
void *(*timer_new_opaque)(SlirpTimerId id, void *cb_opaque, void *opaque);
|
||||
} SlirpCb;
|
||||
|
||||
#define SLIRP_CONFIG_VERSION_MIN 1
|
||||
#define SLIRP_CONFIG_VERSION_MAX 5
|
||||
|
||||
typedef struct SlirpConfig {
|
||||
/* Version must be provided */
|
||||
uint32_t version;
|
||||
/*
|
||||
* Fields introduced in SlirpConfig version 1 begin
|
||||
*/
|
||||
/* Whether to prevent the guest from accessing the Internet */
|
||||
int restricted;
|
||||
/* Whether IPv4 is enabled */
|
||||
bool in_enabled;
|
||||
/* Virtual network for the guest */
|
||||
struct in_addr vnetwork;
|
||||
/* Mask for the virtual network for the guest */
|
||||
struct in_addr vnetmask;
|
||||
/* Virtual address for the host exposed to the guest */
|
||||
struct in_addr vhost;
|
||||
/* Whether IPv6 is enabled */
|
||||
bool in6_enabled;
|
||||
/* Virtual IPv6 network for the guest */
|
||||
struct in6_addr vprefix_addr6;
|
||||
/* Len of the virtual IPv6 network for the guest */
|
||||
uint8_t vprefix_len;
|
||||
/* Virtual address for the host exposed to the guest */
|
||||
struct in6_addr vhost6;
|
||||
/* Hostname exposed to the guest in DHCP hostname option */
|
||||
const char *vhostname;
|
||||
/* Hostname exposed to the guest in the DHCP TFTP server name option */
|
||||
const char *tftp_server_name;
|
||||
/* Path of the files served by TFTP */
|
||||
const char *tftp_path;
|
||||
/* Boot file name exposed to the guest via DHCP */
|
||||
const char *bootfile;
|
||||
/* Start of the DHCP range */
|
||||
struct in_addr vdhcp_start;
|
||||
/* Virtual address for the DNS server exposed to the guest */
|
||||
struct in_addr vnameserver;
|
||||
/* Virtual IPv6 address for the DNS server exposed to the guest */
|
||||
struct in6_addr vnameserver6;
|
||||
/* DNS search names exposed to the guest via DHCP */
|
||||
const char **vdnssearch;
|
||||
/* Domain name exposed to the guest via DHCP */
|
||||
const char *vdomainname;
|
||||
/* MTU when sending packets to the guest */
|
||||
/* Default: IF_MTU_DEFAULT */
|
||||
size_t if_mtu;
|
||||
/* MRU when receiving packets from the guest */
|
||||
/* Default: IF_MRU_DEFAULT */
|
||||
size_t if_mru;
|
||||
/* Prohibit connecting to 127.0.0.1:* */
|
||||
bool disable_host_loopback;
|
||||
/*
|
||||
* Enable emulation code (*warning*: this code isn't safe, it is not
|
||||
* recommended to enable it)
|
||||
*/
|
||||
bool enable_emu;
|
||||
|
||||
/*
|
||||
* Fields introduced in SlirpConfig version 2 begin
|
||||
*/
|
||||
/* Address to be used when sending data to the Internet */
|
||||
struct sockaddr_in *outbound_addr;
|
||||
/* IPv6 Address to be used when sending data to the Internet */
|
||||
struct sockaddr_in6 *outbound_addr6;
|
||||
|
||||
/*
|
||||
* Fields introduced in SlirpConfig version 3 begin
|
||||
*/
|
||||
/* slirp will not redirect/serve any DNS packet */
|
||||
bool disable_dns;
|
||||
|
||||
/*
|
||||
* Fields introduced in SlirpConfig version 4 begin
|
||||
*/
|
||||
/* slirp will not reply to any DHCP requests */
|
||||
bool disable_dhcp;
|
||||
|
||||
/*
|
||||
* Fields introduced in SlirpConfig version 5 begin
|
||||
*/
|
||||
/* Manufacturer ID (IANA Private Enterprise number) */
|
||||
uint32_t mfr_id;
|
||||
/*
|
||||
* MAC address allocated for an out-of-band management controller, to be
|
||||
* retrieved through NC-SI.
|
||||
*/
|
||||
uint8_t oob_eth_addr[6];
|
||||
} SlirpConfig;
|
||||
|
||||
/* Create a new instance of a slirp stack */
|
||||
SLIRP_EXPORT
|
||||
Slirp *slirp_new(const SlirpConfig *cfg, const SlirpCb *callbacks,
|
||||
void *opaque);
|
||||
/* slirp_init is deprecated in favor of slirp_new */
|
||||
SLIRP_EXPORT
|
||||
Slirp *slirp_init(int restricted, bool in_enabled, struct in_addr vnetwork,
|
||||
struct in_addr vnetmask, struct in_addr vhost,
|
||||
bool in6_enabled, struct in6_addr vprefix_addr6,
|
||||
uint8_t vprefix_len, struct in6_addr vhost6,
|
||||
const char *vhostname, const char *tftp_server_name,
|
||||
const char *tftp_path, const char *bootfile,
|
||||
struct in_addr vdhcp_start, struct in_addr vnameserver,
|
||||
struct in6_addr vnameserver6, const char **vdnssearch,
|
||||
const char *vdomainname, const SlirpCb *callbacks,
|
||||
void *opaque);
|
||||
/* Shut down an instance of a slirp stack */
|
||||
SLIRP_EXPORT
|
||||
void slirp_cleanup(Slirp *slirp);
|
||||
|
||||
/* This is called by the application when it is about to sleep through poll().
|
||||
* *timeout is set to the amount of virtual time (in ms) that the application intends to
|
||||
* wait (UINT32_MAX if infinite). slirp_pollfds_fill updates it according to
|
||||
* e.g. TCP timers, so the application knows it should sleep a smaller amount of
|
||||
* time. slirp_pollfds_fill calls add_poll for each file descriptor
|
||||
* that should be monitored along the sleep. The opaque pointer is passed as
|
||||
* such to add_poll, and add_poll returns an index. */
|
||||
SLIRP_EXPORT
|
||||
void slirp_pollfds_fill(Slirp *slirp, uint32_t *timeout,
|
||||
SlirpAddPollCb add_poll, void *opaque);
|
||||
|
||||
/* This is called by the application after sleeping, to report which file
|
||||
* descriptors are available. slirp_pollfds_poll calls get_revents on each file
|
||||
* descriptor, giving it the index that add_poll returned during the
|
||||
* slirp_pollfds_fill call, to know whether the descriptor is available for
|
||||
* read/write/etc. (SLIRP_POLL_*)
|
||||
* select_error should be passed 1 if poll() returned an error. */
|
||||
SLIRP_EXPORT
|
||||
void slirp_pollfds_poll(Slirp *slirp, int select_error,
|
||||
SlirpGetREventsCb get_revents, void *opaque);
|
||||
|
||||
/* This is called by the application when the guest emits a packet on the
|
||||
* guest network, to be interpreted by slirp. */
|
||||
SLIRP_EXPORT
|
||||
void slirp_input(Slirp *slirp, const uint8_t *pkt, int pkt_len);
|
||||
|
||||
/* This is called by the application when a timer expires, if it provides
|
||||
* the timer_new_opaque callback. It is not needed if the application only
|
||||
* uses timer_new. */
|
||||
SLIRP_EXPORT
|
||||
void slirp_handle_timer(Slirp *slirp, SlirpTimerId id, void *cb_opaque);
|
||||
|
||||
/* These set up / remove port forwarding between a host port in the real world
|
||||
* and the guest network. */
|
||||
SLIRP_EXPORT
|
||||
int slirp_add_hostfwd(Slirp *slirp, int is_udp, struct in_addr host_addr,
|
||||
int host_port, struct in_addr guest_addr, int guest_port);
|
||||
SLIRP_EXPORT
|
||||
int slirp_remove_hostfwd(Slirp *slirp, int is_udp, struct in_addr host_addr,
|
||||
int host_port);
|
||||
|
||||
#define SLIRP_HOSTFWD_UDP 1
|
||||
#define SLIRP_HOSTFWD_V6ONLY 2
|
||||
SLIRP_EXPORT
|
||||
int slirp_add_hostxfwd(Slirp *slirp,
|
||||
const struct sockaddr *haddr, socklen_t haddrlen,
|
||||
const struct sockaddr *gaddr, socklen_t gaddrlen,
|
||||
int flags);
|
||||
SLIRP_EXPORT
|
||||
int slirp_remove_hostxfwd(Slirp *slirp,
|
||||
const struct sockaddr *haddr, socklen_t haddrlen,
|
||||
int flags);
|
||||
|
||||
/* Set up port forwarding between a port in the guest network and a
|
||||
* command running on the host */
|
||||
SLIRP_EXPORT
|
||||
int slirp_add_exec(Slirp *slirp, const char *cmdline,
|
||||
struct in_addr *guest_addr, int guest_port);
|
||||
/* Set up port forwarding between a port in the guest network and a
|
||||
* Unix port on the host */
|
||||
SLIRP_EXPORT
|
||||
int slirp_add_unix(Slirp *slirp, const char *unixsock,
|
||||
struct in_addr *guest_addr, int guest_port);
|
||||
/* Set up port forwarding between a port in the guest network and a
|
||||
* callback that will receive the data coming from the port */
|
||||
SLIRP_EXPORT
|
||||
int slirp_add_guestfwd(Slirp *slirp, SlirpWriteCb write_cb, void *opaque,
|
||||
struct in_addr *guest_addr, int guest_port);
|
||||
|
||||
/* TODO: rather identify a guestfwd through an opaque pointer instead of through
|
||||
* the guest_addr */
|
||||
|
||||
/* This is called by the application for a guestfwd, to determine how much data
|
||||
* can be received by the forwarded port through a call to slirp_socket_recv. */
|
||||
SLIRP_EXPORT
|
||||
size_t slirp_socket_can_recv(Slirp *slirp, struct in_addr guest_addr,
|
||||
int guest_port);
|
||||
/* This is called by the application for a guestfwd, to provide the data to be
|
||||
* sent on the forwarded port */
|
||||
SLIRP_EXPORT
|
||||
void slirp_socket_recv(Slirp *slirp, struct in_addr guest_addr, int guest_port,
|
||||
const uint8_t *buf, int size);
|
||||
|
||||
/* Remove entries added by slirp_add_exec, slirp_add_unix or slirp_add_guestfwd */
|
||||
SLIRP_EXPORT
|
||||
int slirp_remove_guestfwd(Slirp *slirp, struct in_addr guest_addr,
|
||||
int guest_port);
|
||||
|
||||
/* Return a human-readable state of the slirp stack */
|
||||
SLIRP_EXPORT
|
||||
char *slirp_connection_info(Slirp *slirp);
|
||||
|
||||
/* Return a human-readable state of the NDP/ARP tables */
|
||||
SLIRP_EXPORT
|
||||
char *slirp_neighbor_info(Slirp *slirp);
|
||||
|
||||
/* Save the slirp state through the write_cb. The opaque pointer is passed as
|
||||
* such to the write_cb. */
|
||||
SLIRP_EXPORT
|
||||
int slirp_state_save(Slirp *s, SlirpWriteCb write_cb, void *opaque);
|
||||
|
||||
/* Returns the version of the slirp state, to be saved along the state */
|
||||
SLIRP_EXPORT
|
||||
int slirp_state_version(void);
|
||||
|
||||
/* Load the slirp state through the read_cb. The opaque pointer is passed as
|
||||
* such to the read_cb. The version should be given as it was obtained from
|
||||
* slirp_state_version when slirp_state_save was called. */
|
||||
SLIRP_EXPORT
|
||||
int slirp_state_load(Slirp *s, int version_id, SlirpReadCb read_cb,
|
||||
void *opaque);
|
||||
|
||||
/* Return the version of the slirp implementation */
|
||||
SLIRP_EXPORT
|
||||
const char *slirp_version_string(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* LIBSLIRP_H */
|
662
usr.sbin/bhyve/net_backend_slirp.c
Normal file
662
usr.sbin/bhyve/net_backend_slirp.c
Normal file
|
@ -0,0 +1,662 @@
|
|||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2023 Mark Johnston <markj@FreeBSD.org>
|
||||
*
|
||||
* This software was developed by the University of Cambridge Computer
|
||||
* Laboratory (Department of Computer Science and Technology) under Innovate
|
||||
* UK project 105694, "Digital Security by Design (DSbD) Technology Platform
|
||||
* Prototype".
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The slirp backend enables unprivileged networking via libslirp, which must be
|
||||
* installed on the host system via pkg or the ports tree. bhyve dlopen()s
|
||||
* libslirp.so upon instantiating the slirp backend. Various network parameters
|
||||
* are hard-coded in _slirp_init().
|
||||
*
|
||||
* Packets received from the guest (i.e., transmitted by the frontend, such as a
|
||||
* virtio NIC device model) are injected into the slirp backend via slirp_send().
|
||||
* Packets to be transmitted to the guest (i.e., inserted into the frontend's
|
||||
* receive buffers) are buffered in a per-interface socket pair and read by the
|
||||
* mevent loop. Sockets instantiated by libslirp are monitored by a thread
|
||||
* which uses poll() and slirp_pollfds_poll() to drive libslirp events; this
|
||||
* thread also handles timeout events from the libslirp context.
|
||||
*/
|
||||
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <capsicum_helpers.h>
|
||||
#include <dlfcn.h>
|
||||
#include <errno.h>
|
||||
#include <poll.h>
|
||||
#include <pthread.h>
|
||||
#include <pthread_np.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "debug.h"
|
||||
#include "libslirp.h"
|
||||
#include "mevent.h"
|
||||
#include "net_backends.h"
|
||||
#include "net_backends_priv.h"
|
||||
|
||||
typedef int (*slirp_add_hostxfwd_p_t)(Slirp *,
|
||||
const struct sockaddr *, socklen_t, const struct sockaddr *, socklen_t,
|
||||
int);
|
||||
typedef void (*slirp_cleanup_p_t)(Slirp *);
|
||||
typedef void (*slirp_input_p_t)(Slirp *, const uint8_t *, int);
|
||||
typedef Slirp *(*slirp_new_p_t)(const SlirpConfig *, const SlirpCb *, void *);
|
||||
typedef void (*slirp_pollfds_fill_p_t)(Slirp *, uint32_t *timeout,
|
||||
SlirpAddPollCb, void *);
|
||||
typedef void (*slirp_pollfds_poll_p_t)(Slirp *, int, SlirpGetREventsCb, void *);
|
||||
|
||||
/* Function pointer table, initialized by slirp_init_once(). */
|
||||
static slirp_add_hostxfwd_p_t slirp_add_hostxfwd_p;
|
||||
static slirp_cleanup_p_t slirp_cleanup_p;
|
||||
static slirp_input_p_t slirp_input_p;
|
||||
static slirp_new_p_t slirp_new_p;
|
||||
static slirp_pollfds_fill_p_t slirp_pollfds_fill_p;
|
||||
static slirp_pollfds_poll_p_t slirp_pollfds_poll_p;
|
||||
|
||||
static int
|
||||
slirp_init_once(void)
|
||||
{
|
||||
static void *handle = NULL;
|
||||
|
||||
if (handle != NULL)
|
||||
return (0);
|
||||
handle = dlopen("libslirp.so.0", RTLD_LAZY);
|
||||
if (handle == NULL) {
|
||||
EPRINTLN("Unable to open libslirp.so.0: %s", dlerror());
|
||||
return (-1);
|
||||
}
|
||||
|
||||
#define IMPORT_SYM(sym) do { \
|
||||
sym##_p = (sym##_p_t)dlsym(handle, #sym); \
|
||||
if (sym##_p == NULL) { \
|
||||
EPRINTLN("failed to resolve %s", #sym); \
|
||||
goto err; \
|
||||
} \
|
||||
} while (0)
|
||||
IMPORT_SYM(slirp_add_hostxfwd);
|
||||
IMPORT_SYM(slirp_cleanup);
|
||||
IMPORT_SYM(slirp_input);
|
||||
IMPORT_SYM(slirp_new);
|
||||
IMPORT_SYM(slirp_pollfds_fill);
|
||||
IMPORT_SYM(slirp_pollfds_poll);
|
||||
#undef IMPORT_SYM
|
||||
|
||||
/*
|
||||
* libslirp uses glib, which uses tzdata to format log messages. Help
|
||||
* it out.
|
||||
*
|
||||
* XXX-MJ glib will also look for charset files, not sure what we can do
|
||||
* about that...
|
||||
*/
|
||||
caph_cache_tzdata();
|
||||
|
||||
return (0);
|
||||
|
||||
err:
|
||||
dlclose(handle);
|
||||
handle = NULL;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
struct slirp_priv {
|
||||
Slirp *slirp;
|
||||
|
||||
#define SLIRP_MTU 2048
|
||||
struct mevent *mevp;
|
||||
int pipe[2];
|
||||
|
||||
pthread_t pollfd_td;
|
||||
struct pollfd *pollfds;
|
||||
size_t npollfds;
|
||||
|
||||
/* Serializes libslirp calls. */
|
||||
pthread_mutex_t mtx;
|
||||
};
|
||||
|
||||
static void
|
||||
slirp_priv_init(struct slirp_priv *priv)
|
||||
{
|
||||
int error;
|
||||
|
||||
memset(priv, 0, sizeof(*priv));
|
||||
priv->pipe[0] = priv->pipe[1] = -1;
|
||||
error = pthread_mutex_init(&priv->mtx, NULL);
|
||||
assert(error == 0);
|
||||
}
|
||||
|
||||
static void
|
||||
slirp_priv_cleanup(struct slirp_priv *priv)
|
||||
{
|
||||
int error;
|
||||
|
||||
if (priv->pipe[0] != -1) {
|
||||
error = close(priv->pipe[0]);
|
||||
assert(error == 0);
|
||||
}
|
||||
if (priv->pipe[1] != -1) {
|
||||
error = close(priv->pipe[1]);
|
||||
assert(error == 0);
|
||||
}
|
||||
if (priv->mevp)
|
||||
mevent_delete(priv->mevp);
|
||||
if (priv->slirp != NULL)
|
||||
slirp_cleanup_p(priv->slirp);
|
||||
error = pthread_mutex_destroy(&priv->mtx);
|
||||
assert(error == 0);
|
||||
}
|
||||
|
||||
static int64_t
|
||||
slirp_cb_clock_get_ns(void *param __unused)
|
||||
{
|
||||
struct timespec ts;
|
||||
int error;
|
||||
|
||||
error = clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
assert(error == 0);
|
||||
return ((int64_t)(ts.tv_sec * 1000000000L + ts.tv_nsec));
|
||||
}
|
||||
|
||||
static void
|
||||
slirp_cb_notify(void *param __unused)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
slirp_cb_register_poll_fd(int fd, void *param __unused)
|
||||
{
|
||||
const int one = 1;
|
||||
|
||||
(void)setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &one, sizeof(int));
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
slirp_cb_send_packet(const void *buf, size_t len, void *param)
|
||||
{
|
||||
struct slirp_priv *priv;
|
||||
ssize_t n;
|
||||
|
||||
priv = param;
|
||||
|
||||
assert(len <= SLIRP_MTU);
|
||||
n = send(priv->pipe[1], buf, len, 0);
|
||||
if (n < 0) {
|
||||
EPRINTLN("slirp_cb_send_packet: send: %s", strerror(errno));
|
||||
return (n);
|
||||
}
|
||||
assert((size_t)n == len);
|
||||
|
||||
return (n);
|
||||
}
|
||||
|
||||
static void
|
||||
slirp_cb_unregister_poll_fd(int fd __unused, void *opaque __unused)
|
||||
{
|
||||
}
|
||||
|
||||
/* Callbacks invoked from within libslirp. */
|
||||
static const struct SlirpCb slirp_cbs = {
|
||||
.clock_get_ns = slirp_cb_clock_get_ns,
|
||||
.notify = slirp_cb_notify,
|
||||
.register_poll_fd = slirp_cb_register_poll_fd,
|
||||
.send_packet = slirp_cb_send_packet,
|
||||
.unregister_poll_fd = slirp_cb_unregister_poll_fd,
|
||||
};
|
||||
|
||||
static int
|
||||
slirpev2pollev(int events)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = 0;
|
||||
if (events & SLIRP_POLL_IN)
|
||||
ret |= POLLIN;
|
||||
if (events & SLIRP_POLL_OUT)
|
||||
ret |= POLLOUT;
|
||||
if (events & SLIRP_POLL_PRI)
|
||||
ret |= POLLPRI;
|
||||
if (events & SLIRP_POLL_ERR)
|
||||
ret |= POLLERR;
|
||||
if (events & SLIRP_POLL_HUP)
|
||||
ret |= POLLHUP;
|
||||
return (ret);
|
||||
}
|
||||
|
||||
static int
|
||||
pollev2slirpev(int events)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = 0;
|
||||
if (events & POLLIN)
|
||||
ret |= SLIRP_POLL_IN;
|
||||
if (events & POLLOUT)
|
||||
ret |= SLIRP_POLL_OUT;
|
||||
if (events & POLLPRI)
|
||||
ret |= SLIRP_POLL_PRI;
|
||||
if (events & POLLERR)
|
||||
ret |= SLIRP_POLL_ERR;
|
||||
if (events & POLLHUP)
|
||||
ret |= SLIRP_POLL_HUP;
|
||||
return (ret);
|
||||
}
|
||||
|
||||
static int
|
||||
slirp_addpoll_cb(int fd, int events, void *param)
|
||||
{
|
||||
struct slirp_priv *priv;
|
||||
struct pollfd *pollfd, *pollfds;
|
||||
size_t i;
|
||||
|
||||
priv = param;
|
||||
|
||||
for (i = 0; i < priv->npollfds; i++)
|
||||
if (priv->pollfds[i].fd == -1)
|
||||
break;
|
||||
if (i == priv->npollfds) {
|
||||
const size_t POLLFD_GROW = 4;
|
||||
|
||||
priv->npollfds += POLLFD_GROW;
|
||||
pollfds = realloc(priv->pollfds,
|
||||
sizeof(*pollfds) * priv->npollfds);
|
||||
if (pollfds == NULL)
|
||||
return (-1);
|
||||
for (i = priv->npollfds - POLLFD_GROW; i < priv->npollfds; i++)
|
||||
pollfds[i].fd = -1;
|
||||
priv->pollfds = pollfds;
|
||||
|
||||
i = priv->npollfds - POLLFD_GROW;
|
||||
}
|
||||
pollfd = &priv->pollfds[i];
|
||||
pollfd->fd = fd;
|
||||
pollfd->events = slirpev2pollev(events);
|
||||
pollfd->revents = 0;
|
||||
|
||||
return ((int)i);
|
||||
}
|
||||
|
||||
static int
|
||||
slirp_poll_revents(int idx, void *param)
|
||||
{
|
||||
struct slirp_priv *priv;
|
||||
struct pollfd *pollfd;
|
||||
|
||||
priv = param;
|
||||
pollfd = &priv->pollfds[idx];
|
||||
assert(pollfd->fd != -1);
|
||||
return (pollev2slirpev(pollfd->revents));
|
||||
}
|
||||
|
||||
static void *
|
||||
slirp_pollfd_td_loop(void *param)
|
||||
{
|
||||
struct slirp_priv *priv;
|
||||
struct pollfd *pollfds;
|
||||
size_t npollfds;
|
||||
uint32_t timeout;
|
||||
int error;
|
||||
|
||||
pthread_set_name_np(pthread_self(), "slirp pollfd");
|
||||
priv = param;
|
||||
|
||||
pthread_mutex_lock(&priv->mtx);
|
||||
for (;;) {
|
||||
for (size_t i = 0; i < priv->npollfds; i++)
|
||||
priv->pollfds[i].fd = -1;
|
||||
|
||||
timeout = UINT32_MAX;
|
||||
slirp_pollfds_fill_p(priv->slirp, &timeout, slirp_addpoll_cb,
|
||||
priv);
|
||||
|
||||
pollfds = priv->pollfds;
|
||||
npollfds = priv->npollfds;
|
||||
pthread_mutex_unlock(&priv->mtx);
|
||||
for (;;) {
|
||||
error = poll(pollfds, npollfds, timeout);
|
||||
if (error == -1) {
|
||||
if (errno != EINTR) {
|
||||
EPRINTLN("poll: %s", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
pthread_mutex_lock(&priv->mtx);
|
||||
slirp_pollfds_poll_p(priv->slirp, error == -1,
|
||||
slirp_poll_revents, priv);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
parse_addr(char *addr, struct sockaddr_in *sinp)
|
||||
{
|
||||
char *port;
|
||||
int error, porti;
|
||||
|
||||
memset(sinp, 0, sizeof(*sinp));
|
||||
sinp->sin_family = AF_INET;
|
||||
sinp->sin_len = sizeof(struct sockaddr_in);
|
||||
|
||||
port = strchr(addr, ':');
|
||||
if (port == NULL)
|
||||
return (EINVAL);
|
||||
*port++ = '\0';
|
||||
|
||||
if (strlen(addr) > 0) {
|
||||
error = inet_pton(AF_INET, addr, &sinp->sin_addr);
|
||||
if (error != 1)
|
||||
return (error == 0 ? EPFNOSUPPORT : errno);
|
||||
} else {
|
||||
sinp->sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
}
|
||||
|
||||
porti = strlen(port) > 0 ? atoi(port) : 0;
|
||||
if (porti < 0 || porti > UINT16_MAX)
|
||||
return (EINVAL);
|
||||
sinp->sin_port = htons(porti);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
parse_hostfwd_rule(const char *descr, int *is_udp, struct sockaddr *hostaddr,
|
||||
struct sockaddr *guestaddr)
|
||||
{
|
||||
struct sockaddr_in *hostaddrp, *guestaddrp;
|
||||
const char *proto;
|
||||
char *p, *host, *guest;
|
||||
int error;
|
||||
|
||||
error = 0;
|
||||
*is_udp = 0;
|
||||
|
||||
p = strdup(descr);
|
||||
if (p == NULL)
|
||||
return (ENOMEM);
|
||||
|
||||
host = strchr(p, ':');
|
||||
if (host == NULL) {
|
||||
error = EINVAL;
|
||||
goto out;
|
||||
}
|
||||
*host++ = '\0';
|
||||
|
||||
proto = p;
|
||||
*is_udp = strcmp(proto, "udp") == 0;
|
||||
|
||||
guest = strchr(host, '-');
|
||||
if (guest == NULL) {
|
||||
error = EINVAL;
|
||||
goto out;
|
||||
}
|
||||
*guest++ = '\0';
|
||||
|
||||
hostaddrp = (struct sockaddr_in *)hostaddr;
|
||||
error = parse_addr(host, hostaddrp);
|
||||
if (error != 0)
|
||||
goto out;
|
||||
|
||||
guestaddrp = (struct sockaddr_in *)guestaddr;
|
||||
error = parse_addr(guest, guestaddrp);
|
||||
if (error != 0)
|
||||
goto out;
|
||||
|
||||
out:
|
||||
free(p);
|
||||
return (error);
|
||||
}
|
||||
|
||||
static int
|
||||
config_one_hostfwd(struct slirp_priv *priv, const char *rule)
|
||||
{
|
||||
struct sockaddr hostaddr, guestaddr;
|
||||
int error, is_udp;
|
||||
|
||||
error = parse_hostfwd_rule(rule, &is_udp, &hostaddr, &guestaddr);
|
||||
if (error != 0) {
|
||||
EPRINTLN("Unable to parse hostfwd rule '%s': %s",
|
||||
rule, strerror(error));
|
||||
return (error);
|
||||
}
|
||||
|
||||
error = slirp_add_hostxfwd_p(priv->slirp, &hostaddr, hostaddr.sa_len,
|
||||
&guestaddr, guestaddr.sa_len, is_udp ? SLIRP_HOSTFWD_UDP : 0);
|
||||
if (error != 0) {
|
||||
EPRINTLN("Unable to add hostfwd rule '%s': %s",
|
||||
rule, strerror(errno));
|
||||
return (error);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
_slirp_init(struct net_backend *be, const char *devname __unused,
|
||||
nvlist_t *nvl, net_be_rxeof_t cb, void *param)
|
||||
{
|
||||
struct slirp_priv *priv = NET_BE_PRIV(be);
|
||||
SlirpConfig config = {
|
||||
.version = 4,
|
||||
.if_mtu = SLIRP_MTU,
|
||||
.restricted = true,
|
||||
.in_enabled = true,
|
||||
.vnetwork.s_addr = htonl(0x0a000200), /* 10.0.2.0/24 */
|
||||
.vnetmask.s_addr = htonl(0xffffff00),
|
||||
.vdhcp_start.s_addr = htonl(0x0a00020f),/* 10.0.2.15 */
|
||||
.vhost.s_addr = htonl(0x0a000202), /* 10.0.2.2 */
|
||||
.enable_emu = false,
|
||||
};
|
||||
const char *hostfwd;
|
||||
int error, sndbuf;
|
||||
|
||||
error = slirp_init_once();
|
||||
if (error != 0)
|
||||
return (error);
|
||||
|
||||
slirp_priv_init(priv);
|
||||
|
||||
priv->slirp = slirp_new_p(&config, &slirp_cbs, priv);
|
||||
if (priv->slirp == NULL) {
|
||||
EPRINTLN("Unable to create slirp instance");
|
||||
goto err;
|
||||
}
|
||||
|
||||
hostfwd = get_config_value_node(nvl, "hostfwd");
|
||||
if (hostfwd != NULL) {
|
||||
char *rules, *tofree;
|
||||
const char *rule;
|
||||
|
||||
tofree = rules = strdup(hostfwd);
|
||||
if (rules == NULL)
|
||||
goto err;
|
||||
while ((rule = strsep(&rules, ";")) != NULL) {
|
||||
error = config_one_hostfwd(priv, rule);
|
||||
if (error != 0)
|
||||
goto err;
|
||||
}
|
||||
free(tofree);
|
||||
}
|
||||
|
||||
error = socketpair(PF_LOCAL, SOCK_DGRAM, 0, priv->pipe);
|
||||
if (error != 0) {
|
||||
EPRINTLN("Unable to create pipe: %s", strerror(errno));
|
||||
goto err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to avoid dropping buffered packets in slirp_cb_send_packet().
|
||||
*/
|
||||
sndbuf = 1024 * 1024;
|
||||
error = setsockopt(priv->pipe[1], SOL_SOCKET, SO_SNDBUF, &sndbuf,
|
||||
sizeof(sndbuf));
|
||||
if (error != 0) {
|
||||
EPRINTLN("Could not set socket buffer size: %s",
|
||||
strerror(errno));
|
||||
goto err;
|
||||
}
|
||||
|
||||
be->fd = priv->pipe[0];
|
||||
priv->mevp = mevent_add_disabled(be->fd, EVF_READ, cb, param);
|
||||
if (priv->mevp == NULL) {
|
||||
EPRINTLN("Could not register event");
|
||||
goto err;
|
||||
}
|
||||
|
||||
error = pthread_create(&priv->pollfd_td, NULL, slirp_pollfd_td_loop,
|
||||
priv);
|
||||
if (error != 0) {
|
||||
EPRINTLN("Unable to create pollfd thread: %s", strerror(error));
|
||||
goto err;
|
||||
}
|
||||
|
||||
return (0);
|
||||
|
||||
err:
|
||||
slirp_priv_cleanup(priv);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
slirp_send(struct net_backend *be, const struct iovec *iov, int iovcnt)
|
||||
{
|
||||
struct slirp_priv *priv = NET_BE_PRIV(be);
|
||||
|
||||
if (iovcnt == 1) {
|
||||
/* We can avoid copying if there's a single segment. */
|
||||
pthread_mutex_lock(&priv->mtx);
|
||||
slirp_input_p(priv->slirp, iov->iov_base,
|
||||
(int)iov->iov_len);
|
||||
pthread_mutex_unlock(&priv->mtx);
|
||||
return (iov[0].iov_len);
|
||||
} else {
|
||||
uint8_t *pkt;
|
||||
size_t pktlen;
|
||||
|
||||
pktlen = 0;
|
||||
for (int i = 0; i < iovcnt; i++)
|
||||
pktlen += iov[i].iov_len;
|
||||
pkt = malloc(pktlen);
|
||||
if (pkt == NULL)
|
||||
return (-1);
|
||||
pktlen = 0;
|
||||
for (int i = 0; i < iovcnt; i++) {
|
||||
memcpy(pkt + pktlen, iov[i].iov_base, iov[i].iov_len);
|
||||
pktlen += iov[i].iov_len;
|
||||
}
|
||||
pthread_mutex_lock(&priv->mtx);
|
||||
slirp_input_p(priv->slirp, pkt, (int)pktlen);
|
||||
pthread_mutex_unlock(&priv->mtx);
|
||||
free(pkt);
|
||||
return (pktlen);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
_slirp_cleanup(struct net_backend *be)
|
||||
{
|
||||
struct slirp_priv *priv = NET_BE_PRIV(be);
|
||||
|
||||
slirp_priv_cleanup(priv);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
slirp_peek_recvlen(struct net_backend *be)
|
||||
{
|
||||
struct slirp_priv *priv = NET_BE_PRIV(be);
|
||||
ssize_t n;
|
||||
|
||||
n = recv(priv->pipe[0], NULL, 0, MSG_PEEK | MSG_DONTWAIT | MSG_TRUNC);
|
||||
if (n < 0)
|
||||
return (errno == EWOULDBLOCK ? 0 : -1);
|
||||
assert((size_t)n <= SLIRP_MTU);
|
||||
return (n);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
slirp_recv(struct net_backend *be, const struct iovec *iov, int iovcnt)
|
||||
{
|
||||
struct slirp_priv *priv = NET_BE_PRIV(be);
|
||||
ssize_t n;
|
||||
|
||||
n = readv(priv->pipe[0], iov, iovcnt);
|
||||
if (n < 0)
|
||||
return (-1);
|
||||
assert(n <= SLIRP_MTU);
|
||||
return (n);
|
||||
}
|
||||
|
||||
static void
|
||||
slirp_recv_enable(struct net_backend *be)
|
||||
{
|
||||
struct slirp_priv *priv = NET_BE_PRIV(be);
|
||||
|
||||
mevent_enable(priv->mevp);
|
||||
}
|
||||
|
||||
static void
|
||||
slirp_recv_disable(struct net_backend *be __unused)
|
||||
{
|
||||
struct slirp_priv *priv = NET_BE_PRIV(be);
|
||||
|
||||
mevent_enable(priv->mevp);
|
||||
}
|
||||
|
||||
static uint64_t
|
||||
slirp_get_cap(struct net_backend *be __unused)
|
||||
{
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
slirp_set_cap(struct net_backend *be __unused, uint64_t features __unused,
|
||||
unsigned int vnet_hdr_len __unused)
|
||||
{
|
||||
return ((features || vnet_hdr_len) ? -1 : 0);
|
||||
}
|
||||
|
||||
static struct net_backend slirp_backend = {
|
||||
.prefix = "slirp",
|
||||
.priv_size = sizeof(struct slirp_priv),
|
||||
.init = _slirp_init,
|
||||
.cleanup = _slirp_cleanup,
|
||||
.send = slirp_send,
|
||||
.peek_recvlen = slirp_peek_recvlen,
|
||||
.recv = slirp_recv,
|
||||
.recv_enable = slirp_recv_enable,
|
||||
.recv_disable = slirp_recv_disable,
|
||||
.get_cap = slirp_get_cap,
|
||||
.set_cap = slirp_set_cap,
|
||||
};
|
||||
|
||||
DATA_SET(net_backend_set, slirp_backend);
|
Loading…
Reference in a new issue