summaryrefslogtreecommitdiffstats
path: root/src/VBox/HostDrivers/VBoxNetAdp/darwin/VBoxNetAdp-darwin.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/HostDrivers/VBoxNetAdp/darwin/VBoxNetAdp-darwin.cpp')
-rw-r--r--src/VBox/HostDrivers/VBoxNetAdp/darwin/VBoxNetAdp-darwin.cpp529
1 files changed, 529 insertions, 0 deletions
diff --git a/src/VBox/HostDrivers/VBoxNetAdp/darwin/VBoxNetAdp-darwin.cpp b/src/VBox/HostDrivers/VBoxNetAdp/darwin/VBoxNetAdp-darwin.cpp
new file mode 100644
index 00000000..ecf4d1f5
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetAdp/darwin/VBoxNetAdp-darwin.cpp
@@ -0,0 +1,529 @@
+/* $Id: VBoxNetAdp-darwin.cpp $ */
+/** @file
+ * VBoxNetAdp - Virtual Network Adapter Driver (Host), Darwin Specific Code.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_NET_ADP_DRV
+#include "../../../Runtime/r0drv/darwin/the-darwin-kernel.h"
+
+#include <VBox/log.h>
+#include <VBox/err.h>
+#include <VBox/version.h>
+#include <iprt/assert.h>
+#include <iprt/initterm.h>
+#include <iprt/semaphore.h>
+#include <iprt/spinlock.h>
+#include <iprt/string.h>
+#include <iprt/uuid.h>
+#include <iprt/alloca.h>
+
+#include "../../darwin/VBoxNetSend.h"
+
+#include <sys/systm.h>
+RT_C_DECLS_BEGIN /* Buggy 10.4 headers, fixed in 10.5. */
+#include <sys/kpi_mbuf.h>
+RT_C_DECLS_END
+
+#include <net/ethernet.h>
+#include <net/if_ether.h>
+#include <net/if_types.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <sys/errno.h>
+#include <sys/param.h>
+#include <sys/conf.h>
+#include <miscfs/devfs/devfs.h>
+RT_C_DECLS_BEGIN
+#include <net/bpf.h>
+RT_C_DECLS_END
+
+#define VBOXNETADP_OS_SPECFIC 1
+#include "../VBoxNetAdpInternal.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** The maximum number of SG segments.
+ * Used to prevent stack overflow and similar bad stuff. */
+#define VBOXNETADP_DARWIN_MAX_SEGS 32
+#define VBOXNETADP_DARWIN_MAX_FAMILIES 4
+#define VBOXNETADP_DARWIN_NAME "vboxnet"
+#define VBOXNETADP_DARWIN_MTU 1500
+#define VBOXNETADP_DARWIN_DETACH_TIMEOUT 500
+
+#define VBOXNETADP_FROM_IFACE(iface) ((PVBOXNETADP) ifnet_softc(iface))
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+RT_C_DECLS_BEGIN
+static kern_return_t VBoxNetAdpDarwinStart(struct kmod_info *pKModInfo, void *pvData);
+static kern_return_t VBoxNetAdpDarwinStop(struct kmod_info *pKModInfo, void *pvData);
+RT_C_DECLS_END
+
+static int VBoxNetAdpDarwinOpen(dev_t Dev, int fFlags, int fDevType, struct proc *pProcess);
+static int VBoxNetAdpDarwinClose(dev_t Dev, int fFlags, int fDevType, struct proc *pProcess);
+static int VBoxNetAdpDarwinIOCtl(dev_t Dev, u_long iCmd, caddr_t pData, int fFlags, struct proc *pProcess);
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/**
+ * Declare the module stuff.
+ */
+RT_C_DECLS_BEGIN
+extern kern_return_t _start(struct kmod_info *pKModInfo, void *pvData);
+extern kern_return_t _stop(struct kmod_info *pKModInfo, void *pvData);
+
+KMOD_EXPLICIT_DECL(VBoxNetAdp, VBOX_VERSION_STRING, _start, _stop)
+DECL_HIDDEN_DATA(kmod_start_func_t *) _realmain = VBoxNetAdpDarwinStart;
+DECL_HIDDEN_DATA(kmod_stop_func_t *) _antimain = VBoxNetAdpDarwinStop;
+DECL_HIDDEN_DATA(int) _kext_apple_cc = __APPLE_CC__;
+RT_C_DECLS_END
+
+/**
+ * The (common) global data.
+ */
+static int g_nCtlDev = -1; /* Major dev number */
+static void *g_hCtlDev = 0; /* FS dev handle */
+
+/**
+ * The character device switch table for the driver.
+ */
+static struct cdevsw g_ChDev =
+{
+ /*.d_open = */VBoxNetAdpDarwinOpen,
+ /*.d_close = */VBoxNetAdpDarwinClose,
+ /*.d_read = */eno_rdwrt,
+ /*.d_write = */eno_rdwrt,
+ /*.d_ioctl = */VBoxNetAdpDarwinIOCtl,
+ /*.d_stop = */eno_stop,
+ /*.d_reset = */eno_reset,
+ /*.d_ttys = */NULL,
+ /*.d_select = */eno_select,
+ /*.d_mmap = */eno_mmap,
+ /*.d_strategy = */eno_strat,
+ /*.d_getc = */(void *)(uintptr_t)&enodev, //eno_getc,
+ /*.d_putc = */(void *)(uintptr_t)&enodev, //eno_putc,
+ /*.d_type = */0
+};
+
+
+
+static void vboxNetAdpDarwinComposeUUID(PVBOXNETADP pThis, PRTUUID pUuid)
+{
+ /* Generate UUID from name and MAC address. */
+ RTUuidClear(pUuid);
+ memcpy(pUuid->au8, "vboxnet", 7);
+ pUuid->Gen.u8ClockSeqHiAndReserved = (pUuid->Gen.u8ClockSeqHiAndReserved & 0x3f) | 0x80;
+ pUuid->Gen.u16TimeHiAndVersion = (pUuid->Gen.u16TimeHiAndVersion & 0x0fff) | 0x4000;
+ pUuid->Gen.u8ClockSeqLow = pThis->iUnit;
+ vboxNetAdpComposeMACAddress(pThis, (PRTMAC)pUuid->Gen.au8Node);
+}
+
+static errno_t vboxNetAdpDarwinOutput(ifnet_t pIface, mbuf_t pMBuf)
+{
+ /*
+ * We are a dummy interface with all the real work done in
+ * VBoxNetFlt bridged networking filter. If anything makes it
+ * this far, it must be a broadcast or a packet for an unknown
+ * guest that intnet didn't know where to dispatch. In that case
+ * we must still do the BPF tap and stats.
+ */
+ bpf_tap_out(pIface, DLT_EN10MB, pMBuf, NULL, 0);
+ ifnet_stat_increment_out(pIface, 1, mbuf_len(pMBuf), 0);
+
+ mbuf_freem_list(pMBuf);
+ return 0;
+}
+
+static void vboxNetAdpDarwinDetach(ifnet_t pIface)
+{
+ PVBOXNETADP pThis = VBOXNETADP_FROM_IFACE(pIface);
+ Assert(pThis);
+ Log2(("vboxNetAdpDarwinDetach: Signaling detach to vboxNetAdpUnregisterDevice.\n"));
+ /* Let vboxNetAdpDarwinUnregisterDevice know that the interface has been detached. */
+ RTSemEventSignal(pThis->u.s.hEvtDetached);
+}
+
+static errno_t vboxNetAdpDarwinDemux(ifnet_t pIface, mbuf_t pMBuf,
+ char *pFrameHeader,
+ protocol_family_t *pProtocolFamily)
+{
+ /*
+ * Anything we get here comes from VBoxNetFlt bridged networking
+ * filter where it has already been accounted for and fed to bpf.
+ */
+ return ether_demux(pIface, pMBuf, pFrameHeader, pProtocolFamily);
+}
+
+
+static errno_t vboxNetAdpDarwinIfIOCtl(ifnet_t pIface, unsigned long uCmd, void *pvData)
+{
+ errno_t error = 0;
+
+ if (pvData == NULL)
+ {
+ /*
+ * Common pattern in the kernel code is to make changes in the
+ * net layer and then notify the device driver by calling its
+ * ioctl function with NULL parameter, e.g.:
+ *
+ * ifnet_set_flags(interface, ...);
+ * ifnet_ioctl(interface, 0, SIOCSIFFLAGS, NULL);
+ *
+ * These are no-ops for us, so tell the caller we succeeded
+ * because some callers do check that return value.
+ */
+ switch (uCmd)
+ {
+ case SIOCSIFFLAGS:
+ Log2(("VBoxNetAdp: %s%d: SIOCSIFFLAGS (null): flags = 0x%04hx\n",
+ ifnet_name(pIface), ifnet_unit(pIface),
+ (uint16_t)ifnet_flags(pIface)));
+ return 0;
+
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ Log2(("VBoxNetAdp: %s%d: SIOC%sMULTI (null)\n",
+ ifnet_name(pIface), ifnet_unit(pIface),
+ uCmd == SIOCADDMULTI ? "ADD" : "DEL"));
+ return 0;
+ }
+ }
+
+ Log2(("VBoxNetAdp: %s%d: %c%c '%c' %u len %u\n",
+ ifnet_name(pIface), ifnet_unit(pIface),
+ uCmd & IOC_OUT ? '<' : '-',
+ uCmd & IOC_IN ? '>' : '-',
+ IOCGROUP(uCmd),
+ uCmd & 0xff,
+ IOCPARM_LEN(uCmd)));
+
+ error = ether_ioctl(pIface, uCmd, pvData);
+ return error;
+}
+
+
+int vboxNetAdpOsCreate(PVBOXNETADP pThis, PCRTMAC pMACAddress)
+{
+ int rc;
+ struct ifnet_init_params Params;
+ RTUUID uuid;
+ struct sockaddr_dl mac;
+
+ pThis->u.s.hEvtDetached = NIL_RTSEMEVENT;
+ rc = RTSemEventCreate(&pThis->u.s.hEvtDetached);
+ if (RT_FAILURE(rc))
+ {
+ printf("vboxNetAdpOsCreate: failed to create semaphore (rc=%d).\n", rc);
+ return rc;
+ }
+
+ mac.sdl_len = sizeof(mac);
+ mac.sdl_family = AF_LINK;
+ mac.sdl_alen = ETHER_ADDR_LEN;
+ mac.sdl_nlen = 0;
+ mac.sdl_slen = 0;
+ memcpy(LLADDR(&mac), pMACAddress->au8, mac.sdl_alen);
+
+ RTStrPrintf(pThis->szName, VBOXNETADP_MAX_NAME_LEN, "%s%d", VBOXNETADP_NAME, pThis->iUnit);
+ vboxNetAdpDarwinComposeUUID(pThis, &uuid);
+ Params.uniqueid = uuid.au8;
+ Params.uniqueid_len = sizeof(uuid);
+ Params.name = VBOXNETADP_NAME;
+ Params.unit = pThis->iUnit;
+ Params.family = IFNET_FAMILY_ETHERNET;
+ Params.type = IFT_ETHER;
+ Params.output = vboxNetAdpDarwinOutput;
+ Params.demux = vboxNetAdpDarwinDemux;
+ Params.add_proto = ether_add_proto;
+ Params.del_proto = ether_del_proto;
+ Params.check_multi = ether_check_multi;
+ Params.framer = ether_frameout;
+ Params.softc = pThis;
+ Params.ioctl = vboxNetAdpDarwinIfIOCtl;
+ Params.set_bpf_tap = NULL;
+ Params.detach = vboxNetAdpDarwinDetach;
+ Params.event = NULL;
+ Params.broadcast_addr = "\xFF\xFF\xFF\xFF\xFF\xFF";
+ Params.broadcast_len = ETHER_ADDR_LEN;
+
+ errno_t err = ifnet_allocate(&Params, &pThis->u.s.pIface);
+ if (!err)
+ {
+ err = ifnet_attach(pThis->u.s.pIface, &mac);
+ if (!err)
+ {
+ bpfattach(pThis->u.s.pIface, DLT_EN10MB, ETHER_HDR_LEN);
+
+ err = ifnet_set_flags(pThis->u.s.pIface, IFF_RUNNING | IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST, 0xFFFF);
+ if (!err)
+ {
+ ifnet_set_mtu(pThis->u.s.pIface, VBOXNETADP_MTU);
+ VBoxNetSendDummy(pThis->u.s.pIface);
+ return VINF_SUCCESS;
+ }
+ else
+ Log(("vboxNetAdpDarwinRegisterDevice: Failed to set flags (err=%d).\n", err));
+ ifnet_detach(pThis->u.s.pIface);
+ }
+ else
+ Log(("vboxNetAdpDarwinRegisterDevice: Failed to attach to interface (err=%d).\n", err));
+ ifnet_release(pThis->u.s.pIface);
+ }
+ else
+ Log(("vboxNetAdpDarwinRegisterDevice: Failed to allocate interface (err=%d).\n", err));
+
+ RTSemEventDestroy(pThis->u.s.hEvtDetached);
+ pThis->u.s.hEvtDetached = NIL_RTSEMEVENT;
+
+ return RTErrConvertFromErrno(err);
+}
+
+void vboxNetAdpOsDestroy(PVBOXNETADP pThis)
+{
+ /* Bring down the interface */
+ int rc = VINF_SUCCESS;
+ errno_t err;
+
+ AssertPtr(pThis->u.s.pIface);
+ Assert(pThis->u.s.hEvtDetached != NIL_RTSEMEVENT);
+
+ err = ifnet_set_flags(pThis->u.s.pIface, 0, IFF_UP | IFF_RUNNING);
+ if (err)
+ Log(("vboxNetAdpDarwinUnregisterDevice: Failed to bring down interface "
+ "(err=%d).\n", err));
+ err = ifnet_detach(pThis->u.s.pIface);
+ if (err)
+ Log(("vboxNetAdpDarwinUnregisterDevice: Failed to detach interface "
+ "(err=%d).\n", err));
+ Log2(("vboxNetAdpDarwinUnregisterDevice: Waiting for 'detached' event...\n"));
+ /* Wait until we get a signal from detach callback. */
+ rc = RTSemEventWait(pThis->u.s.hEvtDetached, VBOXNETADP_DETACH_TIMEOUT);
+ if (rc == VERR_TIMEOUT)
+ LogRel(("VBoxAdpDrv: Failed to detach interface %s%d\n.",
+ VBOXNETADP_NAME, pThis->iUnit));
+ err = ifnet_release(pThis->u.s.pIface);
+ if (err)
+ Log(("vboxNetAdpUnregisterDevice: Failed to release interface (err=%d).\n", err));
+
+ RTSemEventDestroy(pThis->u.s.hEvtDetached);
+ pThis->u.s.hEvtDetached = NIL_RTSEMEVENT;
+}
+
+/**
+ * Device open. Called on open /dev/vboxnetctl
+ *
+ * @param Dev The device number.
+ * @param fFlags ???.
+ * @param fDevType ???.
+ * @param pProcess The process issuing this request.
+ */
+static int VBoxNetAdpDarwinOpen(dev_t Dev, int fFlags, int fDevType, struct proc *pProcess)
+{
+ RT_NOREF(Dev, fFlags, fDevType, pProcess);
+#ifdef LOG_ENABLED
+ char szName[128];
+ szName[0] = '\0';
+ proc_name(proc_pid(pProcess), szName, sizeof(szName));
+ Log(("VBoxNetAdpDarwinOpen: pid=%d '%s'\n", proc_pid(pProcess), szName));
+#endif
+ return 0;
+}
+
+/**
+ * Close device.
+ */
+static int VBoxNetAdpDarwinClose(dev_t Dev, int fFlags, int fDevType, struct proc *pProcess)
+{
+ RT_NOREF(Dev, fFlags, fDevType, pProcess);
+ Log(("VBoxNetAdpDarwinClose: pid=%d\n", proc_pid(pProcess)));
+ return 0;
+}
+
+/**
+ * Device I/O Control entry point.
+ *
+ * @returns Darwin for slow IOCtls and VBox status code for the fast ones.
+ * @param Dev The device number (major+minor).
+ * @param iCmd The IOCtl command.
+ * @param pData Pointer to the data (if any it's a SUPDRVIOCTLDATA (kernel copy)).
+ * @param fFlags Flag saying we're a character device (like we didn't know already).
+ * @param pProcess The process issuing this request.
+ */
+static int VBoxNetAdpDarwinIOCtl(dev_t Dev, u_long iCmd, caddr_t pData, int fFlags, struct proc *pProcess)
+{
+ RT_NOREF(Dev, fFlags, pProcess);
+ uint32_t cbReq = IOCPARM_LEN(iCmd);
+ PVBOXNETADPREQ pReq = (PVBOXNETADPREQ)pData;
+ int rc;
+
+ Log(("VBoxNetAdpDarwinIOCtl: param len %#x; iCmd=%#lx\n", cbReq, iCmd));
+ switch (IOCBASECMD(iCmd))
+ {
+ case IOCBASECMD(VBOXNETADP_CTL_ADD):
+ {
+ if ( (IOC_DIRMASK & iCmd) != IOC_INOUT
+ || cbReq < sizeof(VBOXNETADPREQ))
+ return EINVAL;
+
+ PVBOXNETADP pNew;
+ Log(("VBoxNetAdpDarwinIOCtl: szName=%s\n", pReq->szName));
+ rc = vboxNetAdpCreate(&pNew,
+ pReq->szName[0] && RTStrEnd(pReq->szName, RT_MIN(cbReq, sizeof(pReq->szName))) ?
+ pReq->szName : NULL);
+ if (RT_FAILURE(rc))
+ return rc == VERR_OUT_OF_RESOURCES ? ENOMEM : EINVAL;
+
+ Assert(strlen(pReq->szName) < sizeof(pReq->szName));
+ strncpy(pReq->szName, pNew->szName, sizeof(pReq->szName) - 1);
+ pReq->szName[sizeof(pReq->szName) - 1] = '\0';
+ Log(("VBoxNetAdpDarwinIOCtl: Added '%s'\n", pReq->szName));
+ break;
+ }
+
+ case IOCBASECMD(VBOXNETADP_CTL_REMOVE):
+ {
+ if (!RTStrEnd(pReq->szName, RT_MIN(cbReq, sizeof(pReq->szName))))
+ return EINVAL;
+
+ PVBOXNETADP pAdp = vboxNetAdpFindByName(pReq->szName);
+ if (!pAdp)
+ return EINVAL;
+
+ rc = vboxNetAdpDestroy(pAdp);
+ if (RT_FAILURE(rc))
+ return EINVAL;
+ Log(("VBoxNetAdpDarwinIOCtl: Removed %s\n", pReq->szName));
+ break;
+ }
+
+ default:
+ printf("VBoxNetAdpDarwinIOCtl: unknown command %lx.\n", IOCBASECMD(iCmd));
+ return EINVAL;
+ }
+
+ return 0;
+}
+
+int vboxNetAdpOsInit(PVBOXNETADP pThis)
+{
+ /*
+ * Init the darwin specific members.
+ */
+ pThis->u.s.pIface = NULL;
+ pThis->u.s.hEvtDetached = NIL_RTSEMEVENT;
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Start the kernel module.
+ */
+static kern_return_t VBoxNetAdpDarwinStart(struct kmod_info *pKModInfo, void *pvData)
+{
+ RT_NOREF(pKModInfo, pvData);
+ int rc;
+
+ /*
+ * Initialize IPRT and find our module tag id.
+ * (IPRT is shared with VBoxDrv, it creates the loggers.)
+ */
+ rc = RTR0Init(0);
+ if (RT_SUCCESS(rc))
+ {
+ Log(("VBoxNetAdpDarwinStart\n"));
+ rc = vboxNetAdpInit();
+ if (RT_SUCCESS(rc))
+ {
+ g_nCtlDev = cdevsw_add(-1, &g_ChDev);
+ if (g_nCtlDev < 0)
+ {
+ LogRel(("VBoxAdp: failed to register control device."));
+ rc = VERR_CANT_CREATE;
+ }
+ else
+ {
+ g_hCtlDev = devfs_make_node(makedev(g_nCtlDev, 0), DEVFS_CHAR,
+ UID_ROOT, GID_WHEEL, 0600, VBOXNETADP_CTL_DEV_NAME);
+ if (!g_hCtlDev)
+ {
+ LogRel(("VBoxAdp: failed to create FS node for control device."));
+ rc = VERR_CANT_CREATE;
+ }
+ }
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ LogRel(("VBoxAdpDrv: version " VBOX_VERSION_STRING " r%d\n", VBOX_SVN_REV));
+ return KMOD_RETURN_SUCCESS;
+ }
+
+ LogRel(("VBoxAdpDrv: failed to initialize device extension (rc=%d)\n", rc));
+ RTR0Term();
+ }
+ else
+ printf("VBoxAdpDrv: failed to initialize IPRT (rc=%d)\n", rc);
+
+ return KMOD_RETURN_FAILURE;
+}
+
+
+/**
+ * Stop the kernel module.
+ */
+static kern_return_t VBoxNetAdpDarwinStop(struct kmod_info *pKModInfo, void *pvData)
+{
+ RT_NOREF(pKModInfo, pvData);
+ Log(("VBoxNetAdpDarwinStop\n"));
+
+ vboxNetAdpShutdown();
+ /* Remove control device */
+ devfs_remove(g_hCtlDev);
+ cdevsw_remove(g_nCtlDev, &g_ChDev);
+
+ RTR0Term();
+
+ return KMOD_RETURN_SUCCESS;
+}