Page MenuHomeFreeBSD

D45161.id139080.diff
No OneTemporary

D45161.id139080.diff

diff --git a/usr.sbin/bhyve/bhyve.8 b/usr.sbin/bhyve/bhyve.8
--- a/usr.sbin/bhyve/bhyve.8
+++ b/usr.sbin/bhyve/bhyve.8
@@ -620,6 +620,10 @@
process.
.It Ar /dev/xxx
Use the host TTY device for serial port I/O.
+.It Ar tcp:ip:port
+Use the tcp server for serial port I/O.
+It will start a tcp server and wait for connection.
+Notice that raw tcp has its vulnerability, please use this under LAN
.El
.Ss TPM device backends
.Bl -bullet
@@ -1118,7 +1122,7 @@
.Ed
.Pp
Run a UEFI virtual machine with a display resolution of 800 by 600 pixels
-that can be accessed via VNC at: 0.0.0.0:5900.
+that can be accessed via VNC at: 0.0.0.0:5900 or via raw tcp at: 0.0.0.0:1234.
.Bd -literal -offset indent
bhyve -c 2 -m 4G -w -H \\
-s 0,hostbridge \\
@@ -1127,13 +1131,13 @@
-s 5,virtio-net,tap0 \\
-s 29,fbuf,tcp=0.0.0.0:5900,w=800,h=600,wait \\
-s 30,xhci,tablet \\
- -s 31,lpc -l com1,stdio \\
+ -s 31,lpc -l com1,tcp=0.0.0.0:1234 \\
-l bootrom,/usr/local/share/uefi-firmware/BHYVE_UEFI.fd \\
uefivm
.Ed
.Pp
Run a UEFI virtual machine with a VNC display that is bound to all IPv6
-addresses on port 5900.
+addresses on port 5900 and a TCP serial I/O bound to port 1234.
.Bd -literal -offset indent
bhyve -c 2 -m 4G -w -H \\
-s 0,hostbridge \\
@@ -1141,7 +1145,7 @@
-s 5,virtio-net,tap0 \\
-s 29,fbuf,tcp=[::]:5900,w=800,h=600 \\
-s 30,xhci,tablet \\
- -s 31,lpc -l com1,stdio \\
+ -s 31,lpc -l com1,tcp=[::]:1234 \\
-l bootrom,/usr/local/share/uefi-firmware/BHYVE_UEFI.fd \\
uefivm
.Ed
diff --git a/usr.sbin/bhyve/uart_backend.c b/usr.sbin/bhyve/uart_backend.c
--- a/usr.sbin/bhyve/uart_backend.c
+++ b/usr.sbin/bhyve/uart_backend.c
@@ -28,13 +28,18 @@
*/
#include <sys/types.h>
+#include <sys/socket.h>
#include <machine/vmm.h>
#include <machine/vmm_snapshot.h>
+#include <netinet/in.h>
+
+#include <arpa/inet.h>
#include <assert.h>
#include <capsicum_helpers.h>
#include <err.h>
+#include <netdb.h>
#include <pthread.h>
#include <stdbool.h>
#include <stdlib.h>
@@ -49,6 +54,7 @@
struct ttyfd {
bool opened;
+ bool connect_based;
int rfd; /* fd for reading */
int wfd; /* fd for writing, may be == rfd */
};
@@ -70,9 +76,17 @@
pthread_mutex_t mtx;
};
+struct uart_connect_infos {
+ struct uart_softc *softc;
+ void (*drain)(int, enum ev_type, void *);
+ void *arg;
+};
+
static bool uart_stdio; /* stdio in use for i/o */
static struct termios tio_stdio_orig;
+static void uart_tcp_disconnect(struct uart_softc *);
+
static void
ttyclose(void)
{
@@ -97,20 +111,22 @@
}
static int
-ttyread(struct ttyfd *tf)
+ttyread(struct ttyfd *tf, uint8_t *ret)
{
- unsigned char rb;
+ uint8_t rb;
+ int len;
- if (read(tf->rfd, &rb, 1) == 1)
- return (rb);
- else
- return (-1);
+ len = read(tf->rfd, &rb, 1);
+ if (ret)
+ *ret = rb;
+
+ return (len);
}
-static void
+static int
ttywrite(struct ttyfd *tf, unsigned char wb)
{
- (void)write(tf->wfd, &wb, 1);
+ return (write(tf->wfd, &wb, 1));
}
static bool
@@ -179,14 +195,27 @@
void
uart_rxfifo_drain(struct uart_softc *sc, bool loopback)
{
- int ch;
+ uint8_t ch;
+ int len;
if (loopback) {
- (void)ttyread(&sc->tty);
+ if (ttyread(&sc->tty, &ch) == 0 && sc->tty.connect_based)
+ uart_tcp_disconnect(sc);
} else {
- while (rxfifo_available(sc) &&
- ((ch = ttyread(&sc->tty)) != -1))
+ while (rxfifo_available(sc)) {
+ len = ttyread(&sc->tty, &ch);
+ if (len <= 0) {
+ /*
+ * If return value of read is 0, it means
+ * disconnected
+ */
+ if (len == 0 && sc->tty.connect_based)
+ uart_tcp_disconnect(sc);
+ break;
+ }
+
rxfifo_putchar(sc, ch);
+ }
}
}
@@ -196,7 +225,11 @@
if (loopback) {
return (rxfifo_putchar(sc, ch));
} else if (sc->tty.opened) {
- ttywrite(&sc->tty, ch);
+ /*
+ * We the return value of write is -1, it means disconnected
+ */
+ if (ttywrite(&sc->tty, ch) == -1 && sc->tty.connect_based)
+ uart_tcp_disconnect(sc);
return (0);
} else {
/* Drop on the floor. */
@@ -259,6 +292,61 @@
}
#endif
+/*
+ * Listen to the tcp port, wait connection, then accept it
+ */
+static void
+uart_tcp_listener(int fd, enum ev_type type __unused, void *arg)
+{
+ const static char tcp_error_msg[] = "Socket already connected\n";
+ struct uart_connect_infos *connect_infos = (struct uart_connect_infos *)
+ arg;
+ struct uart_softc *sc = connect_infos->softc;
+ int conn_fd;
+
+ conn_fd = accept(fd, NULL, NULL);
+ if (conn_fd == -1) {
+ errx(EX_OSERR, "Unable to accept the connection");
+ return;
+ }
+
+ if (fcntl(conn_fd, F_SETFL, O_NONBLOCK) != 0) {
+ errx(EX_OSERR, "Unable to set nonblocking mode to connection");
+ return;
+ }
+
+ pthread_mutex_lock(&sc->mtx);
+
+ if (sc->tty.opened) {
+ send(conn_fd, tcp_error_msg, sizeof(tcp_error_msg), 0);
+ close(conn_fd);
+ } else {
+ sc->tty.rfd = sc->tty.wfd = conn_fd;
+ sc->tty.opened = true;
+ ttyopen(&sc->tty);
+ sc->mev = mevent_add(sc->tty.rfd, EVF_READ,
+ connect_infos->drain, connect_infos->arg);
+ }
+
+ pthread_mutex_unlock(&sc->mtx);
+}
+
+/*
+ * When connected based protocal disconnected, we use this handler to clean it
+ * Notice that this function is a helper function thus the caller should
+ * take the responsibilty to lock the softc
+ */
+static void
+uart_tcp_disconnect(struct uart_softc *sc)
+{
+ mevent_delete_close(sc->mev);
+ sc->mev = NULL;
+
+ sc->tty.opened = false;
+ close(sc->tty.rfd);
+ sc->tty.rfd = sc->tty.wfd = -1;
+}
+
static int
uart_stdio_backend(struct uart_softc *sc)
{
@@ -324,6 +412,95 @@
return (0);
}
+/*
+ * Listen to address and add to kqueue.
+ * If connected (e.g. tcp_handler is triggered), we replace the handler to the
+ * connected handler
+ */
+static int
+uart_tcp_backend(struct uart_softc *sc, const char *path,
+ void (*drain)(int, enum ev_type, void *), void *arg)
+{
+#ifndef WITHOUT_CAPSICUM
+ cap_rights_t rights;
+ cap_ioctl_t cmds[] = { TIOCGETA, TIOCSETA, TIOCGWINSZ };
+#endif
+ int bind_fd;
+ char addr[256], port[6];
+ int domain;
+ struct addrinfo hints, *src_addr;
+ struct uart_connect_infos *connect_infos;
+
+ if (sscanf(path, "tcp=[%255[^]]]:%5s", addr, port) == 2) {
+ domain = AF_INET6;
+ } else if (sscanf(path, "tcp=%255[^:]:%5s", addr, port) == 2) {
+ domain = AF_INET;
+ } else {
+ errx(EX_CONFIG, "Invalid number of parameter");
+ return (-1);
+ }
+
+ bind_fd = socket(domain, SOCK_STREAM, 0);
+ if (bind_fd < 0)
+ return (-1);
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = domain;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV | AI_PASSIVE;
+
+ if (getaddrinfo(addr, port, &hints, &src_addr) != 0) {
+ errx(EX_CONFIG, "Invalid address %s:%s", addr, port);
+ return (-1);
+ }
+
+ if (bind(bind_fd, src_addr->ai_addr, src_addr->ai_addrlen) == -1) {
+ errx(EX_OSERR,
+ "Unable to bind to address %s:%s. Maybe the address is used?",
+ addr, port);
+ return (-1);
+ }
+
+ if (fcntl(bind_fd, F_SETFL, O_NONBLOCK) == -1)
+ return (-1);
+
+ if (listen(bind_fd, 1) == -1) {
+ errx(EX_OSERR, "Unable to listen to address %s:%s", addr, port);
+ return (-1);
+ }
+
+ /*
+ * Set connect infos, this structure includes both softc and drain
+ * function provided by the frontend
+ */
+ if ((connect_infos = calloc(sizeof(struct uart_connect_infos), 1)) ==
+ NULL)
+ return (-1);
+
+ connect_infos->softc = sc;
+ connect_infos->drain = drain;
+ connect_infos->arg = arg;
+
+ if ((sc->mev = mevent_add(bind_fd, EVF_READ, uart_tcp_listener,
+ connect_infos)) == NULL) {
+ free(connect_infos);
+ return (-1);
+ }
+
+#ifndef WITHOUT_CAPSICUM
+ cap_rights_init(&rights, CAP_EVENT, CAP_ACCEPT, CAP_RECV, CAP_SEND,
+ CAP_FCNTL, CAP_IOCTL);
+ if (caph_rights_limit(bind_fd, &rights) == -1)
+ errx(EX_OSERR, "Unable to apply rights for sandbox");
+ if (caph_ioctls_limit(bind_fd, cmds, nitems(cmds)) == -1)
+ errx(EX_OSERR, "Unable to apply ioctls for sandbox");
+ if (caph_fcntls_limit(bind_fd, CAP_FCNTL_SETFL) == -1)
+ errx(EX_OSERR, "Unable to apply fcntls for sandbox");
+#endif
+
+ return (0);
+}
+
struct uart_softc *
uart_init(void)
{
@@ -342,11 +519,19 @@
{
int retval;
- if (strcmp("stdio", path) == 0)
+ if (strcmp("stdio", path) == 0) {
retval = uart_stdio_backend(sc);
- else
+ } else if (strncmp("tcp", path, 3) == 0) {
+ retval = uart_tcp_backend(sc, path, drain, arg);
+ sc->tty.connect_based = true;
+ } else {
retval = uart_tty_backend(sc, path);
- if (retval == 0) {
+ }
+ /*
+ * Connected based protocal should wait for connection
+ * Thus it can listen nothing when initialize
+ */
+ if (retval == 0 && !sc->tty.connect_based) {
ttyopen(&sc->tty);
sc->mev = mevent_add(sc->tty.rfd, EVF_READ, drain, arg);
assert(sc->mev != NULL);

File Metadata

Mime Type
text/plain
Expires
Sat, Jan 17, 6:29 PM (13 h, 36 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
27697632
Default Alt Text
D45161.id139080.diff (8 KB)

Event Timeline