The branch main has been updated by markj:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=cbc6f7e941e42639a0314cd121b06493cce8e0e6

commit cbc6f7e941e42639a0314cd121b06493cce8e0e6
Author:     Quentin Thébault <[email protected]>
AuthorDate: 2025-11-18 06:44:05 +0000
Commit:     Mark Johnston <[email protected]>
CommitDate: 2026-01-08 15:24:52 +0000

    bhyve: add UNIX domain socket support to rfb
    
    This commit adds support for a UNIX domain socket to bhyve's remote
    framebuffer. It enables the use of the graphical console when the bhyve 
instance
    is running in a jail with no networking, for instance. A VNC client running 
on
    the host can then connect to the UNIX domain socket through the filesystem.
    
    Signed-off-by:  Quentin Thébault <[email protected]>
    Sponsored by:   Defenso
    Reviewed by:    kevans, markj
    MFC after:      2 weeks
    Differential Revision: https://reviews.freebsd.org/D53814
---
 usr.sbin/bhyve/bhyve.8        | 12 +++++++----
 usr.sbin/bhyve/bhyve_config.5 |  7 ++++++-
 usr.sbin/bhyve/pci_fbuf.c     | 22 ++++++++++++++++++-
 usr.sbin/bhyve/rfb.c          | 49 +++++++++++++++++++++++++++++++------------
 usr.sbin/bhyve/rfb.h          |  4 +++-
 5 files changed, 74 insertions(+), 20 deletions(-)

diff --git a/usr.sbin/bhyve/bhyve.8 b/usr.sbin/bhyve/bhyve.8
index d3b067509ced..496539f30885 100644
--- a/usr.sbin/bhyve/bhyve.8
+++ b/usr.sbin/bhyve/bhyve.8
@@ -891,7 +891,7 @@ to guest by VirtIO Input Interface.
 .Bl -bullet
 .Sm off
 .It
-.Op Cm rfb= Ar ip-and-port
+.Op Cm rfb= Ar address
 .Op Cm ,w= Ar width
 .Op Cm ,h= Ar height
 .Op Cm ,vga= Ar vgaconf
@@ -902,9 +902,9 @@ to guest by VirtIO Input Interface.
 .Pp
 Configuration options are defined as follows:
 .Bl -tag -width 10n
-.It Cm rfb= Ns Ar ip-and-port Pq or Cm tcp= Ns Ar ip-and-port
-An IP address and a port VNC should listen on.
-There are two formats:
+.It Cm rfb= Ns Ar address Pq or Cm tcp= Ns Ar address
+A UNIX domain socket or IP address and a port VNC should listen on.
+There are three possible formats:
 .Pp
 .Bl -bullet -compact
 .It
@@ -916,6 +916,10 @@ There are two formats:
 .Sm off
 .Cm \&[ Ar IPv6%zone Cm \&] Cm \&: Ar port
 .Sm on
+.It
+.Sm off
+.Cm unix: Ar my/unix.sock
+.Sm on
 .El
 .Pp
 The default is to listen on localhost IPv4 address and default VNC port 5900.
diff --git a/usr.sbin/bhyve/bhyve_config.5 b/usr.sbin/bhyve/bhyve_config.5
index 4ead94690d91..429ce3e38138 100644
--- a/usr.sbin/bhyve/bhyve_config.5
+++ b/usr.sbin/bhyve/bhyve_config.5
@@ -523,7 +523,7 @@ the device name.
 If specified, it must be a unicast MAC address.
 .El
 .Ss Frame Buffer Settings
-.Bl -column "password" "[IP:]port" "127.0.0.1:5900"
+.Bl -column "password" "unix:my/unix.sock" "127.0.0.1:5900"
 .It Sy Name Ta Sy Format Ta Sy Default Ta Sy Description
 .It Va wait Ta bool Ta false Ta
 Wait for a remote connection before starting the VM.
@@ -535,6 +535,11 @@ support scoped identifiers as described in
 .Xr getaddrinfo 3 .
 A bare port number may be given in which case the IPv4
 localhost address is used.
+.It Va rfb Ta
+.Sm off
+.Cm unix: Ar my/unix.sock Ta Ta
+.Sm on
+Alternatively, provide a path to a UNIX domain socket.
 .It Va vga Ta string Ta io Ta
 VGA configuration.
 More details are provided in
diff --git a/usr.sbin/bhyve/pci_fbuf.c b/usr.sbin/bhyve/pci_fbuf.c
index 1e3ec77c15b0..560c2bc839d6 100644
--- a/usr.sbin/bhyve/pci_fbuf.c
+++ b/usr.sbin/bhyve/pci_fbuf.c
@@ -28,6 +28,8 @@
 
 #include <sys/types.h>
 #include <sys/mman.h>
+#include <sys/socket.h>
+#include <sys/un.h>
 
 #include <dev/vmm/vmm_mem.h>
 #include <machine/vmm.h>
@@ -94,6 +96,7 @@ struct pci_fbuf_softc {
        } __packed memregs;
 
        /* rfb server */
+       sa_family_t rfb_family;
        char      *rfb_host;
        char      *rfb_password;
        int       rfb_port;
@@ -252,11 +255,13 @@ pci_fbuf_parse_config(struct pci_fbuf_softc *sc, nvlist_t 
*nvl)
                value = get_config_value_node(nvl, "tcp");
        if (value != NULL) {
                /*
+                * UNIX -- unix:path/to/socket.sock
                 * IPv4 -- host-ip:port
                 * IPv6 -- [host-ip%zone]:port
                 * XXX for now port is mandatory for IPv4.
                 */
                if (value[0] == '[') {
+                       sc->rfb_family = AF_INET6;
                        cp = strchr(value + 1, ']');
                        if (cp == NULL || cp == value + 1) {
                                EPRINTLN("fbuf: Invalid IPv6 address: \"%s\"",
@@ -279,7 +284,21 @@ pci_fbuf_parse_config(struct pci_fbuf_softc *sc, nvlist_t 
*nvl)
                                    value);
                                return (-1);
                        }
+               } else if (strncmp("unix:", value, 5) == 0) {
+                       if (strlen(value + 5) > SUNPATHLEN) {
+                               EPRINTLN(
+                                   "fbuf: UNIX socket path too long: \"%s\"",
+                                   value + 5);
+                               return (-1);
+                       } else if (*(value + 5) == '\0') {
+                               EPRINTLN("fbuf: UNIX socket path is empty");
+                               return (-1);
+                       } else {
+                               sc->rfb_family = AF_UNIX;
+                               sc->rfb_host = strdup(value + 5);
+                       }
                } else {
+                       sc->rfb_family = AF_UNSPEC;
                        cp = strchr(value, ':');
                        if (cp == NULL) {
                                sc->rfb_port = atoi(value);
@@ -433,7 +452,8 @@ pci_fbuf_init(struct pci_devinst *pi, nvlist_t *nvl)
 
        memset((void *)sc->fb_base, 0, FB_SIZE);
 
-       error = rfb_init(sc->rfb_host, sc->rfb_port, sc->rfb_wait, 
sc->rfb_password);
+       error = rfb_init(sc->rfb_family, sc->rfb_host, sc->rfb_port,
+           sc->rfb_wait, sc->rfb_password);
 done:
        if (error)
                free(sc);
diff --git a/usr.sbin/bhyve/rfb.c b/usr.sbin/bhyve/rfb.c
index 716e191e2fc0..aeaf8d1c0639 100644
--- a/usr.sbin/bhyve/rfb.c
+++ b/usr.sbin/bhyve/rfb.c
@@ -35,6 +35,7 @@
 #include <sys/socket.h>
 #include <sys/select.h>
 #include <sys/time.h>
+#include <sys/un.h>
 #include <arpa/inet.h>
 #include <stdatomic.h>
 #include <machine/cpufunc.h>
@@ -1254,13 +1255,15 @@ sse42_supported(void)
 }
 
 int
-rfb_init(const char *hostname, int port, int wait, const char *password)
+rfb_init(sa_family_t family, const char *hostname, int port, int wait,
+    const char *password)
 {
        int e;
        char servname[6];
        struct rfb_softc *rc;
        struct addrinfo *ai = NULL;
        struct addrinfo hints;
+       struct sockaddr_un sun;
        int on = 1;
        int cnt;
 #ifndef WITHOUT_CAPSICUM
@@ -1301,25 +1304,42 @@ rfb_init(const char *hostname, int port, int wait, 
const char *password)
                hostname = "[::1]";
 #endif
 
-       memset(&hints, 0, sizeof(hints));
-       hints.ai_family = AF_UNSPEC;
-       hints.ai_socktype = SOCK_STREAM;
-       hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV | AI_PASSIVE;
-
-       if ((e = getaddrinfo(hostname, servname, &hints, &ai)) != 0) {
-               EPRINTLN("getaddrinfo: %s", gai_strerror(e));
-               goto error;
+       if (family == AF_UNIX) {
+               memset(&sun, 0, sizeof(sun));
+               sun.sun_family = AF_UNIX;
+               if (strlcpy(sun.sun_path, hostname, sizeof(sun.sun_path)) >=
+                   sizeof(sun.sun_path)) {
+                       EPRINTLN("rfb: socket path too long");
+                       goto error;
+               }
+               rc->sfd = socket(AF_UNIX, SOCK_STREAM, 0);
+       } else {
+               memset(&hints, 0, sizeof(hints));
+               hints.ai_socktype = SOCK_STREAM;
+               hints.ai_family = family;
+               hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV | AI_PASSIVE;
+
+               if ((e = getaddrinfo(hostname, servname, &hints, &ai)) != 0) {
+                       EPRINTLN("getaddrinfo: %s", gai_strerror(e));
+                       goto error;
+               }
+               rc->sfd = socket(ai->ai_family, ai->ai_socktype, 0);
        }
 
-       rc->sfd = socket(ai->ai_family, ai->ai_socktype, 0);
        if (rc->sfd < 0) {
                perror("socket");
                goto error;
        }
 
+       /* No effect for UNIX domain sockets. */
        setsockopt(rc->sfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
 
-       if (bind(rc->sfd, ai->ai_addr, ai->ai_addrlen) < 0) {
+       if (family == AF_UNIX) {
+               unlink(hostname);
+               e = bind(rc->sfd, (struct sockaddr *)&sun, SUN_LEN(&sun));
+       } else
+               e = bind(rc->sfd, ai->ai_addr, ai->ai_addrlen);
+       if (e < 0) {
                perror("bind");
                goto error;
        }
@@ -1355,14 +1375,17 @@ rfb_init(const char *hostname, int port, int wait, 
const char *password)
                DPRINTF(("rfb client connected"));
        }
 
-       freeaddrinfo(ai);
+       if (family != AF_UNIX)
+               freeaddrinfo(ai);
        return (0);
 
  error:
        if (rc->pixfmt_mtx)
                pthread_mutex_destroy(&rc->pixfmt_mtx);
-       if (ai != NULL)
+       if (ai != NULL) {
+               assert(family != AF_UNIX);
                freeaddrinfo(ai);
+       }
        if (rc->sfd != -1)
                close(rc->sfd);
        free(rc->crc);
diff --git a/usr.sbin/bhyve/rfb.h b/usr.sbin/bhyve/rfb.h
index 347ced083a22..c11d40f031af 100644
--- a/usr.sbin/bhyve/rfb.h
+++ b/usr.sbin/bhyve/rfb.h
@@ -29,9 +29,11 @@
 #ifndef _RFB_H_
 #define        _RFB_H_
 
+#include <sys/socket.h>
+
 #define        RFB_PORT        5900
 
-int    rfb_init(const char *hostname, int port, int wait,
+int    rfb_init(sa_family_t family, const char *hostname, int port, int wait,
            const char *password);
 
 #endif /* _RFB_H_ */

Reply via email to