From f215e02bf85f68d3a6106c2a1f4f7f063f819064 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Thu, 11 Apr 2024 10:17:27 +0200 Subject: Adding upstream version 7.0.14-dfsg. Signed-off-by: Daniel Baumann --- src/VBox/HostDrivers/VBoxNetAdp/Makefile.kmk | 228 +++ src/VBox/HostDrivers/VBoxNetAdp/VBoxNetAdp.c | 238 +++ .../HostDrivers/VBoxNetAdp/VBoxNetAdpInternal.h | 202 +++ src/VBox/HostDrivers/VBoxNetAdp/darwin/Info.plist | 26 + .../HostDrivers/VBoxNetAdp/darwin/Makefile.kup | 0 .../VBoxNetAdp/darwin/VBoxNetAdp-darwin.cpp | 529 ++++++ .../HostDrivers/VBoxNetAdp/darwin/loadnetadp.sh | 133 ++ src/VBox/HostDrivers/VBoxNetAdp/freebsd/Makefile | 54 + .../VBoxNetAdp/freebsd/VBoxNetAdp-freebsd.c | 339 ++++ .../VBoxNetAdp/freebsd/files_vboxnetadp | 94 + src/VBox/HostDrivers/VBoxNetAdp/linux/Makefile | 78 + src/VBox/HostDrivers/VBoxNetAdp/linux/Makefile.kup | 0 .../VBoxNetAdp/linux/VBoxNetAdp-linux.c | 583 ++++++ .../HostDrivers/VBoxNetAdp/linux/files_vboxnetadp | 112 ++ .../VBoxNetAdp/solaris/VBoxNetAdp-solaris.c | 581 ++++++ .../VBoxNetAdp/solaris/hostname.vboxnet0 | 1 + .../HostDrivers/VBoxNetAdp/solaris/vboxnet.conf | 43 + src/VBox/HostDrivers/VBoxNetAdp/win/Makefile.kup | 0 .../HostDrivers/VBoxNetAdp/win/VBoxNetAdp-win.cpp | 1888 ++++++++++++++++++++ .../HostDrivers/VBoxNetAdp/win/VBoxNetAdp-win.h | 71 + .../HostDrivers/VBoxNetAdp/win/VBoxNetAdp-win.rc | 72 + .../HostDrivers/VBoxNetAdp/win/VBoxNetAdp6.inf | 101 ++ 22 files changed, 5373 insertions(+) create mode 100644 src/VBox/HostDrivers/VBoxNetAdp/Makefile.kmk create mode 100644 src/VBox/HostDrivers/VBoxNetAdp/VBoxNetAdp.c create mode 100644 src/VBox/HostDrivers/VBoxNetAdp/VBoxNetAdpInternal.h create mode 100644 src/VBox/HostDrivers/VBoxNetAdp/darwin/Info.plist create mode 100644 src/VBox/HostDrivers/VBoxNetAdp/darwin/Makefile.kup create mode 100644 src/VBox/HostDrivers/VBoxNetAdp/darwin/VBoxNetAdp-darwin.cpp create mode 100755 src/VBox/HostDrivers/VBoxNetAdp/darwin/loadnetadp.sh create mode 100644 src/VBox/HostDrivers/VBoxNetAdp/freebsd/Makefile create mode 100644 src/VBox/HostDrivers/VBoxNetAdp/freebsd/VBoxNetAdp-freebsd.c create mode 100755 src/VBox/HostDrivers/VBoxNetAdp/freebsd/files_vboxnetadp create mode 100644 src/VBox/HostDrivers/VBoxNetAdp/linux/Makefile create mode 100644 src/VBox/HostDrivers/VBoxNetAdp/linux/Makefile.kup create mode 100644 src/VBox/HostDrivers/VBoxNetAdp/linux/VBoxNetAdp-linux.c create mode 100755 src/VBox/HostDrivers/VBoxNetAdp/linux/files_vboxnetadp create mode 100644 src/VBox/HostDrivers/VBoxNetAdp/solaris/VBoxNetAdp-solaris.c create mode 100644 src/VBox/HostDrivers/VBoxNetAdp/solaris/hostname.vboxnet0 create mode 100644 src/VBox/HostDrivers/VBoxNetAdp/solaris/vboxnet.conf create mode 100644 src/VBox/HostDrivers/VBoxNetAdp/win/Makefile.kup create mode 100644 src/VBox/HostDrivers/VBoxNetAdp/win/VBoxNetAdp-win.cpp create mode 100644 src/VBox/HostDrivers/VBoxNetAdp/win/VBoxNetAdp-win.h create mode 100644 src/VBox/HostDrivers/VBoxNetAdp/win/VBoxNetAdp-win.rc create mode 100644 src/VBox/HostDrivers/VBoxNetAdp/win/VBoxNetAdp6.inf (limited to 'src/VBox/HostDrivers/VBoxNetAdp') diff --git a/src/VBox/HostDrivers/VBoxNetAdp/Makefile.kmk b/src/VBox/HostDrivers/VBoxNetAdp/Makefile.kmk new file mode 100644 index 00000000..62c8baf4 --- /dev/null +++ b/src/VBox/HostDrivers/VBoxNetAdp/Makefile.kmk @@ -0,0 +1,228 @@ +# $Id: Makefile.kmk $ +## @file +# Sub-Makefile for the Network Adapter Driver (VBoxNetAdp). +# + +# +# Copyright (C) 2009-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 . +# +# 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 +# + +SUB_DEPTH = ../../../.. +include $(KBUILD_PATH)/subheader.kmk + +if1of ($(KBUILD_TARGET), solaris darwin freebsd) + # + # VBoxNetAdp - Virtual Network Adapter + # Note! On Solaris the name has to be <= 8 chars long. + # + ifdef VBOX_WITH_VBOXDRV + SYSMODS += VBoxNetAdp + VBoxNetAdp_TEMPLATE = VBoxR0Drv + VBoxNetAdp_INST = $(INST_VBOXNETADP)$(if $(eq $(KBUILD_TARGET),darwin),Contents/MacOS/) + VBoxNetAdp_DEBUG_INST.darwin = $(patsubst %/,%,$(INST_VBOXNETADP)) + VBoxNetAdp_NAME.solaris = vboxnet + VBoxNetAdp_NAME.freebsd = vboxnetadp + VBoxNetAdp_DEPS.solaris += $(VBOX_SVN_REV_KMK) + VBoxNetAdp_DEFS = IN_RT_R0 VBOX_SVN_REV=$(VBOX_SVN_REV) IN_SUP_STATIC + #VBoxNetAdp_LDFLAGS.darwin = -v -Wl,-whyload -Wl,-v -Wl,-whatsloaded + VBoxNetAdp_LDFLAGS.solaris += -N misc/gld -N drv/vboxdrv + VBoxNetAdp_INCS = \ + . + VBoxNetAdp_SOURCES.darwin = \ + darwin/VBoxNetAdp-darwin.cpp \ + VBoxNetAdp.c + VBoxNetAdp_SOURCES.solaris = \ + solaris/VBoxNetAdp-solaris.c + VBoxNetAdp_SOURCES.freebsd = \ + freebsd/VBoxNetAdp-freebsd.c \ + VBoxNetAdp.c + VBoxNetAdp_SOURCES = + #VBoxNetAdp_SOURCES = \ + # VBoxNetAdp.c + VBoxNetAdp_LIBS += \ + $(PATH_STAGE_LIB)/SUPR0IdcClient$(VBOX_SUFF_LIB) + endif +endif + +# +# Darwin extras. +# +if "$(KBUILD_TARGET)" == "darwin" && defined(VBOX_WITH_VBOXDRV) + INSTALLS += VBoxNetAdp.kext + VBoxNetAdp.kext_INST = $(INST_VBOXNETADP)Contents/ + VBoxNetAdp.kext_SOURCES = $(VBoxNetAdp.kext_0_OUTDIR)/Contents/Info.plist + VBoxNetAdp.kext_CLEAN = $(VBoxNetAdp.kext_0_OUTDIR)/Contents/Info.plist + VBoxNetAdp.kext_BLDDIRS = $(VBoxNetAdp.kext_0_OUTDIR)/Contents/ + + $$(VBoxNetAdp.kext_0_OUTDIR)/Contents/Info.plist: $(PATH_SUB_CURRENT)/darwin/Info.plist $(VBOX_VERSION_MK) | $$(dir $$@) + $(call MSG_GENERATE,VBoxNetAdp,$@,$<) + $(QUIET)$(RM) -f $@ + $(QUIET)$(SED) \ + -e 's+@VBOX_VERSION_STRING@+$(VBOX_VERSION_STRING)+g' \ + -e 's+@VBOX_VERSION_MAJOR@+$(VBOX_VERSION_MAJOR)+g' \ + -e 's+@VBOX_VERSION_MINOR@+$(VBOX_VERSION_MINOR)+g' \ + -e 's+@VBOX_VERSION_BUILD@+$(VBOX_VERSION_BUILD)+g' \ + -e 's+@VBOX_VENDOR@+$(VBOX_VENDOR)+g' \ + -e 's+@VBOX_PRODUCT@+$(VBOX_PRODUCT)+g' \ + -e 's+@VBOX_C_YEAR@+$(VBOX_C_YEAR)+g' \ + --output $@ \ + $< + + $(evalcall2 VBOX_TEST_SIGN_KEXT,VBoxNetAdp) + + INSTALLS.darwin += Scripts-darwin-adp + Scripts-darwin-adp_INST = $(INST_DIST) + Scripts-darwin-adp_EXEC_SOURCES = \ + darwin/loadnetadp.sh +endif # darwin && host-drivers + +ifeq ($(KBUILD_TARGET),win) + # + # VBoxNetAdp6.sys - The VirtualBox Adapter miniport driver. + # + SYSMODS.win += VBoxNetAdp6 + VBoxNetAdp6_TEMPLATE = VBoxR0Drv + if defined(VBOX_SIGNING_MODE) + VBoxNetAdp6_INSTTYPE.win = none + VBoxNetAdp6_DEBUG_INSTTYPE.win = both + endif + VBoxNetAdp6_DEFS = IN_RT_R0 IN_SUP_STATIC + VBoxNetAdp6_INCS := $(PATH_SUB_CURRENT) + VBoxNetAdp6_SDKS = ReorderCompilerIncs $(VBOX_WINDDK_WLH) $(VBOX_WINPSDK_INCS) + VBoxNetAdp6_SOURCES = \ + win/VBoxNetAdp-win.cpp \ + win/VBoxNetAdp-win.rc + VBoxNetAdp6_DEFS += NDIS_MINIPORT_DRIVER NDIS_WDM=1 BINARY_COMPATIBLE=0 + VBoxNetAdp6_DEFS += NDIS60_MINIPORT=1 NDIS60=1 + VBoxNetAdp6_LDFLAGS.win.x86 = -Entry:DriverEntry@8 + VBoxNetAdp6_LDFLAGS.win.amd64 = -Entry:DriverEntry + VBoxNetAdp6_LIBS.win = \ + $(PATH_SDK_$(VBOX_WINDDK)_LIB)/ntoskrnl.lib \ + $(PATH_SDK_$(VBOX_WINDDK)_LIB)/hal.lib \ + $(PATH_SDK_$(VBOX_WINDDK)_LIB)/ndis.lib \ + $(PATH_STAGE_LIB)/RuntimeR0Drv$(VBOX_SUFF_LIB) + VBoxNetAdp6_LIBS = \ + $(PATH_STAGE_LIB)/SUPR0IdcClient$(VBOX_SUFF_LIB) + + + INSTALLS.win += VBoxNetAdp6-inf + VBoxNetAdp6-inf_TEMPLATE = VBoxR0DrvInfCat + VBoxNetAdp6-inf_SOURCES = \ + $(PATH_TARGET)/VBoxNetAdp6Cat.dir/VBoxNetAdp6.inf + VBoxNetAdp6-inf_CLEAN = $(VBoxNetAdp6-inf_SOURCES) + VBoxNetAdp6-inf_BLDDIRS = $(PATH_TARGET)/VBoxNetAdp6Cat.dir + + $(PATH_TARGET)/VBoxNetAdp6Cat.dir/VBoxNetAdp6.inf: $(PATH_SUB_CURRENT)/win/VBoxNetAdp6.inf $(MAKEFILE_CURRENT) | $$(dir $$@) + $(call MSG_GENERATE,VBoxNetAdp6-inf,$@,$<) + $(call VBOX_EDIT_INF_FN,$<,$@) + + ifdef VBOX_SIGNING_MODE + VBoxNetAdp6-inf_SOURCES += \ + $(PATH_TARGET)/VBoxNetAdp6Cat.dir/VBoxNetAdp6.sys \ + $(PATH_TARGET)/VBoxNetAdp6Cat.dir/VBoxNetAdp6.cat \ + $(PATH_TARGET)/VBoxNetAdp6Cat.dir/VBoxNetAdp6.cat=>VBoxNetAdp6-PreW10.cat + + $(PATH_TARGET)/VBoxNetAdp6Cat.dir/VBoxNetAdp6.sys: $$(VBoxNetAdp6_1_TARGET) | $$(dir $$@) + $(INSTALL) -m 644 $< $(@D) + + $(PATH_TARGET)/VBoxNetAdp6Cat.dir/VBoxNetAdp6.cat: \ + $(PATH_TARGET)/VBoxNetAdp6Cat.dir/VBoxNetAdp6.sys \ + $(PATH_TARGET)/VBoxNetAdp6Cat.dir/VBoxNetAdp6.inf + $(call MSG_TOOL,Inf2Cat,VBoxNetFlt-inf,$@,$<) + $(call VBOX_MAKE_CAT_FN, $(@D),$@) + + endif # ifdef VBOX_SIGNING_MODE + +endif #ifeq ($(KBUILD_TARGET), win) + +ifeq ($(KBUILD_TARGET),linux) + # + # Install source files for compilation on Linux. + # files_vboxnetadp defines VBOX_VBOXNETADP_SOURCES. + # + INSTALLS += VBoxNetAdp-src + include $(PATH_SUB_CURRENT)/linux/files_vboxnetadp + VBoxNetAdp-src_INST = bin/src/vboxnetadp/ + VBoxNetAdp-src_SOURCES = \ + $(subst $(DQUOTE),,$(VBOX_VBOXNETADP_SOURCES)) \ + $(VBoxNetAdp-src_0_OUTDIR)/Makefile + VBoxNetAdp-src_CLEAN = \ + $(VBoxNetAdp-src_0_OUTDIR)/Makefile \ + $(PATH_TARGET)/VBoxNetAdp-src-1.dep + + # Scripts needed for building the kernel modules + includedep $(PATH_TARGET)/VBoxNetAdp-src-1.dep + $$(VBoxNetAdp-src_0_OUTDIR)/Makefile: \ + $(PATH_SUB_CURRENT)/linux/Makefile \ + $$(if $$(eq $$(VBoxNetAdp/linux/Makefile_VBOX_HARDENED),$$(VBOX_WITH_HARDENING)),,FORCE) \ + | $$(dir $$@) + $(QUIET)$(RM) -f -- $@ + ifndef VBOX_WITH_HARDENING + $(QUIET)$(SED) -e "s;VBOX_WITH_HARDENING;;g" --output $@ $< + else + $(QUIET)$(CP) -f $< $@ + endif + %$(QUIET2)$(APPEND) -t '$(PATH_TARGET)/VBoxNetAdp-src-1.dep' 'VBoxNetAdp/linux/Makefile_VBOX_HARDENED=$(VBOX_WITH_HARDENING)' + + # + # Build test for the linux host kernel modules. + # + $(evalcall2 VBOX_LINUX_KMOD_TEST_BUILD_RULE_FN,VBoxNetAdp-src,vboxdrv-src,) +endif # linux + +ifeq ($(KBUILD_TARGET),freebsd) + # + # Install source files for compilation on FreeBSD. + # files_vboxnetadp defines VBOX_VBOXNETADP_SOURCES. + # + INSTALLS += VBoxNetAdp-src + include $(PATH_SUB_CURRENT)/freebsd/files_vboxnetadp + VBoxNetAdp-src_INST = bin/src/vboxnetadp/ + VBoxNetAdp-src_SOURCES = \ + $(subst $(DQUOTE),,$(VBOX_VBOXNETADP_SOURCES)) \ + $(VBoxNetAdp-src_0_OUTDIR)/Makefile + VBoxNetAdp-src_CLEAN = \ + $(VBoxNetAdp-src_0_OUTDIR)/Makefile + + $$(VBoxNetAdp-src_0_OUTDIR)/Makefile: \ + $(PATH_SUB_CURRENT)/freebsd/Makefile \ + $$(if $$(eq $$(VBoxNetAdp/freebsd/Makefile_VBOX_HARDENED),$$(VBOX_WITH_HARDENING)),,FORCE) \ + | $$(dir $$@) + $(QUIET)$(RM) -f -- $@ + ifndef VBOX_WITH_HARDENING + $(QUIET)$(SED) -e "s;VBOX_WITH_HARDENING;;g" --output $@ $< + else + $(QUIET)$(CP) -f $< $@ + endif + +endif # freebsd + +include $(FILE_KBUILD_SUB_FOOTER) + diff --git a/src/VBox/HostDrivers/VBoxNetAdp/VBoxNetAdp.c b/src/VBox/HostDrivers/VBoxNetAdp/VBoxNetAdp.c new file mode 100644 index 00000000..ea3f7766 --- /dev/null +++ b/src/VBox/HostDrivers/VBoxNetAdp/VBoxNetAdp.c @@ -0,0 +1,238 @@ +/* $Id: VBoxNetAdp.c $ */ +/** @file + * VBoxNetAdp - Virtual Network Adapter Driver (Host), Common 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 . + * + * 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 + */ + +/** @page pg_netadp VBoxNetAdp - Network Adapter + * + * This is a kernel module that creates a virtual interface that can be attached + * to an internal network. + * + * In the big picture we're one of the three trunk interface on the internal + * network, the one named "TAP Interface": @image html Networking_Overview.gif + * + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP LOG_GROUP_NET_ADP_DRV +#include "VBoxNetAdpInternal.h" + +#include +#include +#include + + +VBOXNETADP g_aAdapters[VBOXNETADP_MAX_INSTANCES]; +static uint8_t g_aUnits[VBOXNETADP_MAX_UNITS/8]; + + +DECLINLINE(int) vboxNetAdpGetUnitByName(const char *pcszName) +{ + uint32_t iUnit = RTStrToUInt32(pcszName + sizeof(VBOXNETADP_NAME) - 1); + bool fOld; + + if (iUnit >= VBOXNETADP_MAX_UNITS) + return -1; + + fOld = ASMAtomicBitTestAndSet(g_aUnits, iUnit); + return fOld ? -1 : (int)iUnit; +} + +DECLINLINE(int) vboxNetAdpGetNextAvailableUnit(void) +{ + bool fOld; + int iUnit; + /* There is absolutely no chance that all units are taken */ + do { + iUnit = ASMBitFirstClear(g_aUnits, VBOXNETADP_MAX_UNITS); + if (iUnit < 0) + break; + fOld = ASMAtomicBitTestAndSet(g_aUnits, iUnit); + } while (fOld); + + return iUnit; +} + +DECLINLINE(void) vboxNetAdpReleaseUnit(int iUnit) +{ + bool fSet = ASMAtomicBitTestAndClear(g_aUnits, iUnit); + NOREF(fSet); + Assert(fSet); +} + +/** + * Generate a suitable MAC address. + * + * @param pThis The instance. + * @param pMac Where to return the MAC address. + */ +DECLHIDDEN(void) vboxNetAdpComposeMACAddress(PVBOXNETADP pThis, PRTMAC pMac) +{ + /* Use a locally administered version of the OUI we use for the guest NICs. */ + pMac->au8[0] = 0x08 | 2; + pMac->au8[1] = 0x00; + pMac->au8[2] = 0x27; + + pMac->au8[3] = 0; /* pThis->iUnit >> 16; */ + pMac->au8[4] = 0; /* pThis->iUnit >> 8; */ + pMac->au8[5] = pThis->iUnit; +} + +int vboxNetAdpCreate(PVBOXNETADP *ppNew, const char *pcszName) +{ + int rc; + unsigned i; + for (i = 0; i < RT_ELEMENTS(g_aAdapters); i++) + { + PVBOXNETADP pThis = &g_aAdapters[i]; + + if (ASMAtomicCmpXchgU32((uint32_t volatile *)&pThis->enmState, kVBoxNetAdpState_Transitional, kVBoxNetAdpState_Invalid)) + { + RTMAC Mac; + /* Found an empty slot -- use it. */ + Log(("vboxNetAdpCreate: found empty slot: %d\n", i)); + if (pcszName) + { + Log(("vboxNetAdpCreate: using name: %s\n", pcszName)); + pThis->iUnit = vboxNetAdpGetUnitByName(pcszName); + strncpy(pThis->szName, pcszName, sizeof(pThis->szName) - 1); + pThis->szName[sizeof(pThis->szName) - 1] = '\0'; + } + else + { + pThis->iUnit = vboxNetAdpGetNextAvailableUnit(); + pThis->szName[0] = '\0'; + } + if (pThis->iUnit < 0) + rc = VERR_INVALID_PARAMETER; + else + { + vboxNetAdpComposeMACAddress(pThis, &Mac); + rc = vboxNetAdpOsCreate(pThis, &Mac); + Log(("vboxNetAdpCreate: pThis=%p pThis->iUnit=%d, pThis->szName=%s\n", + pThis, pThis->iUnit, pThis->szName)); + } + if (RT_SUCCESS(rc)) + { + *ppNew = pThis; + ASMAtomicWriteU32((uint32_t volatile *)&pThis->enmState, kVBoxNetAdpState_Active); + Log2(("VBoxNetAdpCreate: Created %s\n", g_aAdapters[i].szName)); + } + else + { + ASMAtomicWriteU32((uint32_t volatile *)&pThis->enmState, kVBoxNetAdpState_Invalid); + Log(("vboxNetAdpCreate: vboxNetAdpOsCreate failed with '%Rrc'.\n", rc)); + } + for (i = 0; i < RT_ELEMENTS(g_aAdapters); i++) + Log2(("VBoxNetAdpCreate: Scanning entry: state=%d unit=%d name=%s\n", + g_aAdapters[i].enmState, g_aAdapters[i].iUnit, g_aAdapters[i].szName)); + return rc; + } + } + Log(("vboxNetAdpCreate: no empty slots!\n")); + + /* All slots in adapter array are busy. */ + return VERR_OUT_OF_RESOURCES; +} + +int vboxNetAdpDestroy(PVBOXNETADP pThis) +{ + int rc = VINF_SUCCESS; + + if (!ASMAtomicCmpXchgU32((uint32_t volatile *)&pThis->enmState, kVBoxNetAdpState_Transitional, kVBoxNetAdpState_Active)) + return VERR_INTNET_FLT_IF_BUSY; + + Assert(pThis->iUnit >= 0 && pThis->iUnit < VBOXNETADP_MAX_UNITS); + vboxNetAdpOsDestroy(pThis); + vboxNetAdpReleaseUnit(pThis->iUnit); + pThis->iUnit = -1; + pThis->szName[0] = '\0'; + + ASMAtomicWriteU32((uint32_t volatile *)&pThis->enmState, kVBoxNetAdpState_Invalid); + + return rc; +} + +int vboxNetAdpInit(void) +{ + unsigned i; + /* + * Init common members and call OS-specific init. + */ + memset(g_aUnits, 0, sizeof(g_aUnits)); + memset(g_aAdapters, 0, sizeof(g_aAdapters)); + LogFlow(("vboxnetadp: max host-only interfaces supported: %d (%d bytes)\n", + VBOXNETADP_MAX_INSTANCES, sizeof(g_aAdapters))); + for (i = 0; i < RT_ELEMENTS(g_aAdapters); i++) + { + g_aAdapters[i].enmState = kVBoxNetAdpState_Invalid; + g_aAdapters[i].iUnit = -1; + vboxNetAdpOsInit(&g_aAdapters[i]); + } + + return VINF_SUCCESS; +} + +/** + * Finds an adapter by its name. + * + * @returns Pointer to the instance by the given name. NULL if not found. + * @param pszName The name of the instance. + */ +PVBOXNETADP vboxNetAdpFindByName(const char *pszName) +{ + unsigned i; + + for (i = 0; i < RT_ELEMENTS(g_aAdapters); i++) + { + PVBOXNETADP pThis = &g_aAdapters[i]; + Log2(("VBoxNetAdp: Scanning entry: state=%d name=%s\n", pThis->enmState, pThis->szName)); + if ( strcmp(pThis->szName, pszName) == 0 + && ASMAtomicReadU32((uint32_t volatile *)&pThis->enmState) == kVBoxNetAdpState_Active) + return pThis; + } + return NULL; +} + +void vboxNetAdpShutdown(void) +{ + unsigned i; + + /* Remove virtual adapters */ + for (i = 0; i < RT_ELEMENTS(g_aAdapters); i++) + vboxNetAdpDestroy(&g_aAdapters[i]); +} diff --git a/src/VBox/HostDrivers/VBoxNetAdp/VBoxNetAdpInternal.h b/src/VBox/HostDrivers/VBoxNetAdp/VBoxNetAdpInternal.h new file mode 100644 index 00000000..d50fa039 --- /dev/null +++ b/src/VBox/HostDrivers/VBoxNetAdp/VBoxNetAdpInternal.h @@ -0,0 +1,202 @@ +/* $Id: VBoxNetAdpInternal.h $ */ +/** @file + * VBoxNetAdp - Network Filter Driver (Host), Internal Header. + */ + +/* + * 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 . + * + * 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 + */ + +#ifndef VBOX_INCLUDED_SRC_VBoxNetAdp_VBoxNetAdpInternal_h +#define VBOX_INCLUDED_SRC_VBoxNetAdp_VBoxNetAdpInternal_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#include +#include +#include +#include + + +RT_C_DECLS_BEGIN + +/** Pointer to the globals. */ +typedef struct VBOXNETADPGLOBALS *PVBOXNETADPGLOBALS; + +#define VBOXNETADP_MAX_INSTANCES 128 +#define VBOXNETADP_MAX_UNITS 128 +#define VBOXNETADP_NAME "vboxnet" +#define VBOXNETADP_MAX_NAME_LEN 32 +#define VBOXNETADP_MTU 1500 +#if defined(RT_OS_DARWIN) +# define VBOXNETADP_MAX_FAMILIES 4 +# define VBOXNETADP_DETACH_TIMEOUT 500 +#endif + +#define VBOXNETADP_CTL_DEV_NAME "vboxnetctl" +#define VBOXNETADP_CTL_ADD _IOWR('v', 1, VBOXNETADPREQ) +#define VBOXNETADP_CTL_REMOVE _IOW('v', 2, VBOXNETADPREQ) + +typedef struct VBoxNetAdpReq +{ + char szName[VBOXNETADP_MAX_NAME_LEN]; +} VBOXNETADPREQ; +typedef VBOXNETADPREQ *PVBOXNETADPREQ; + +/** + * Void entries mark vacant slots in adapter array. Valid entries are busy slots. + * As soon as slot is being modified its state changes to transitional. + * An entry in transitional state must only be accessed by the thread that + * put it to this state. + */ +/** + * To avoid races on adapter fields we stick to the following rules: + * - rewrite: Int net port calls are serialized + * - No modifications are allowed on busy adapters (deactivate first) + * Refuse to destroy adapter until it gets to available state + * - No transfers (thus getting busy) on inactive adapters + * - Init sequence: void->available->connected->active + 1) Create + 2) Connect + 3) Activate + * - Destruction sequence: active->connected->available->void + 1) Deactivate + 2) Disconnect + 3) Destroy +*/ + +enum VBoxNetAdpState +{ + kVBoxNetAdpState_Invalid, + kVBoxNetAdpState_Transitional, + kVBoxNetAdpState_Active, + kVBoxNetAdpState_32BitHack = 0x7FFFFFFF +}; +typedef enum VBoxNetAdpState VBOXNETADPSTATE; + +struct VBoxNetAdapter +{ + /** Denotes availability of this slot in adapter array. */ + VBOXNETADPSTATE enmState; + /** Corresponds to the digit at the end of device name. */ + int iUnit; + + union + { +#ifdef VBOXNETADP_OS_SPECFIC + struct + { +# if defined(RT_OS_DARWIN) + /** @name Darwin instance data. + * @{ */ + /** Event to signal detachment of interface. */ + RTSEMEVENT hEvtDetached; + /** Pointer to Darwin interface structure. */ + ifnet_t pIface; + /** MAC address. */ + RTMAC Mac; + /** @} */ +# elif defined(RT_OS_LINUX) + /** @name Darwin instance data. + * @{ */ + /** Pointer to Linux network device structure. */ + struct net_device *pNetDev; + /** @} */ +# elif defined(RT_OS_FREEBSD) + /** @name FreeBSD instance data. + * @{ */ + struct ifnet *ifp; + /** @} */ +# else +# error PORTME +# endif + } s; +#endif + /** Union alignment to a pointer. */ + void *pvAlign; + /** Padding. */ + uint8_t abPadding[64]; + } u; + /** The interface name. */ + char szName[VBOXNETADP_MAX_NAME_LEN]; +}; +typedef struct VBoxNetAdapter VBOXNETADP; +typedef VBOXNETADP *PVBOXNETADP; +/* Paranoia checks for alignment and padding. */ +AssertCompileMemberAlignment(VBOXNETADP, u, ARCH_BITS/8); +AssertCompileMemberAlignment(VBOXNETADP, szName, ARCH_BITS/8); +AssertCompileMembersSameSize(VBOXNETADP, u, VBOXNETADP, u.abPadding); + +DECLHIDDEN(int) vboxNetAdpInit(void); +DECLHIDDEN(void) vboxNetAdpShutdown(void); +DECLHIDDEN(int) vboxNetAdpCreate(PVBOXNETADP *ppNew, const char *pcszName); +DECLHIDDEN(int) vboxNetAdpDestroy(PVBOXNETADP pThis); +DECLHIDDEN(PVBOXNETADP) vboxNetAdpFindByName(const char *pszName); +DECLHIDDEN(void) vboxNetAdpComposeMACAddress(PVBOXNETADP pThis, PRTMAC pMac); + + +/** + * This is called to perform OS-specific structure initializations. + * + * @return IPRT status code. + * @param pThis The new instance. + * + * @remarks Owns no locks. + */ +DECLHIDDEN(int) vboxNetAdpOsInit(PVBOXNETADP pThis); + +/** + * Counter part to vboxNetAdpOsCreate(). + * + * @param pThis The new instance. + * + * @remarks May own the semaphores for the global list, the network lock and the out-bound trunk port. + */ +DECLHIDDEN(void) vboxNetAdpOsDestroy(PVBOXNETADP pThis); + +/** + * This is called to attach to the actual host interface + * after linking the instance into the list. + * + * @return IPRT status code. + * @param pThis The new instance. + * @param pMac The MAC address to use for this instance. + * + * @remarks Owns no locks. + */ +DECLHIDDEN(int) vboxNetAdpOsCreate(PVBOXNETADP pThis, PCRTMAC pMac); + + + +RT_C_DECLS_END + +#endif /* !VBOX_INCLUDED_SRC_VBoxNetAdp_VBoxNetAdpInternal_h */ + diff --git a/src/VBox/HostDrivers/VBoxNetAdp/darwin/Info.plist b/src/VBox/HostDrivers/VBoxNetAdp/darwin/Info.plist new file mode 100644 index 00000000..c0b20e7b --- /dev/null +++ b/src/VBox/HostDrivers/VBoxNetAdp/darwin/Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion English + CFBundleExecutable VBoxNetAdp + CFBundleIdentifier org.virtualbox.kext.VBoxNetAdp + CFBundleInfoDictionaryVersion 6.0 + CFBundleName VBoxNetAdp + CFBundlePackageType KEXT + CFBundleSignature ???? + CFBundleGetInfoString @VBOX_PRODUCT@ @VBOX_VERSION_STRING@, © 2008-@VBOX_C_YEAR@ @VBOX_VENDOR@ + CFBundleVersion @VBOX_VERSION_MAJOR@.@VBOX_VERSION_MINOR@.@VBOX_VERSION_BUILD@ + CFBundleShortVersionString @VBOX_VERSION_MAJOR@.@VBOX_VERSION_MINOR@.@VBOX_VERSION_BUILD@ + IOKitPersonalities + + + OSBundleLibraries + + com.apple.kpi.bsd 8.8.1 + com.apple.kpi.mach 8.8.1 + com.apple.kpi.libkern 8.8.1 + org.virtualbox.kext.VBoxDrv @VBOX_VERSION_MAJOR@.@VBOX_VERSION_MINOR@.@VBOX_VERSION_BUILD@ + + + diff --git a/src/VBox/HostDrivers/VBoxNetAdp/darwin/Makefile.kup b/src/VBox/HostDrivers/VBoxNetAdp/darwin/Makefile.kup new file mode 100644 index 00000000..e69de29b 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 . + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../../darwin/VBoxNetSend.h" + +#include +RT_C_DECLS_BEGIN /* Buggy 10.4 headers, fixed in 10.5. */ +#include +RT_C_DECLS_END + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +RT_C_DECLS_BEGIN +#include +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; +} diff --git a/src/VBox/HostDrivers/VBoxNetAdp/darwin/loadnetadp.sh b/src/VBox/HostDrivers/VBoxNetAdp/darwin/loadnetadp.sh new file mode 100755 index 00000000..b1e28d94 --- /dev/null +++ b/src/VBox/HostDrivers/VBoxNetAdp/darwin/loadnetadp.sh @@ -0,0 +1,133 @@ +#!/bin/bash +## @file +# For development. +# + +# +# Copyright (C) 2006-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 . +# +# 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 +# + +SCRIPT_NAME="loadnetadp" +XNU_VERSION=`LC_ALL=C uname -r | LC_ALL=C cut -d . -f 1` + +DRVNAME="VBoxNetAdp.kext" +BUNDLE="org.virtualbox.kext.VBoxNetAdp" + +DEP_DRVNAME="VBoxDrv.kext" +DEP_BUNDLE="org.virtualbox.kext.VBoxDrv" + + +DIR=`dirname "$0"` +DIR=`cd "$DIR" && pwd` +DEP_DIR="$DIR/$DEP_DRVNAME" +DIR="$DIR/$DRVNAME" +if [ ! -d "$DIR" ]; then + echo "Cannot find $DIR or it's not a directory..." + exit 1; +fi +if [ ! -d "$DEP_DIR" ]; then + echo "Cannot find $DEP_DIR or it's not a directory... (dependency)" + exit 1; +fi +if [ -n "$*" ]; then + OPTS="$*" +else + OPTS="-t" +fi + +trap "sudo chown -R `whoami` $DIR $DEP_DIR; exit 1" INT + +# Try unload any existing instance first. +LOADED=`kextstat -b $BUNDLE -l` +if test -n "$LOADED"; then + echo "${SCRIPT_NAME}.sh: Unloading $BUNDLE..." + sudo kextunload -v 6 -b $BUNDLE + LOADED=`kextstat -b $BUNDLE -l` + if test -n "$LOADED"; then + echo "${SCRIPT_NAME}.sh: failed to unload $BUNDLE, see above..." + exit 1; + fi + echo "${SCRIPT_NAME}.sh: Successfully unloaded $BUNDLE" +fi + +set -e + +# Copy the .kext to the symbols directory and tweak the kextload options. +if test -n "$VBOX_DARWIN_SYMS"; then + echo "${SCRIPT_NAME}.sh: copying the extension the symbol area..." + rm -Rf "$VBOX_DARWIN_SYMS/$DRVNAME" + mkdir -p "$VBOX_DARWIN_SYMS" + cp -R "$DIR" "$VBOX_DARWIN_SYMS/" + OPTS="$OPTS -s $VBOX_DARWIN_SYMS/ " + sync +fi + +# On smbfs, this might succeed just fine but make no actual changes, +# so we might have to temporarily copy the driver to a local directory. +if sudo chown -R root:wheel "$DIR" "$DEP_DIR"; then + OWNER=`/usr/bin/stat -f "%u" "$DIR"` +else + OWNER=1000 +fi +if test "$OWNER" -ne 0; then + TMP_DIR=/tmp/${SCRIPT_NAME}.tmp + echo "${SCRIPT_NAME}.sh: chown didn't work on $DIR, using temp location $TMP_DIR/$DRVNAME" + + # clean up first (no sudo rm) + if test -e "$TMP_DIR"; then + sudo chown -R `whoami` "$TMP_DIR" + rm -Rf "$TMP_DIR" + fi + + # make a copy and switch over DIR + mkdir -p "$TMP_DIR/" + sudo cp -Rp "$DIR" "$TMP_DIR/" + DIR="$TMP_DIR/$DRVNAME" + + # load.sh puts it here. + DEP_DIR="/tmp/loaddrv.tmp/$DEP_DRVNAME" + + # retry + sudo chown -R root:wheel "$DIR" "$DEP_DIR" +fi + +sudo chmod -R o-rwx "$DIR" +sync +if [ "$XNU_VERSION" -ge "10" ]; then + echo "${SCRIPT_NAME}.sh: loading $DIR... (kextutil $OPTS -d \"$DEP_DIR\" \"$DIR\")" + sudo kextutil $OPTS -d "$DEP_DIR" "$DIR" +else + echo "${SCRIPT_NAME}.sh: loading $DIR... (kextload $OPTS -d \"$DEP_DIR\" \"$DIR\")" +sudo kextload $OPTS -d "$DEP_DIR" "$DIR" +fi +sync +sudo chown -R `whoami` "$DIR" "$DEP_DIR" +kextstat | grep org.virtualbox.kext + diff --git a/src/VBox/HostDrivers/VBoxNetAdp/freebsd/Makefile b/src/VBox/HostDrivers/VBoxNetAdp/freebsd/Makefile new file mode 100644 index 00000000..013a223d --- /dev/null +++ b/src/VBox/HostDrivers/VBoxNetAdp/freebsd/Makefile @@ -0,0 +1,54 @@ +# $Id: Makefile $ +## @file +# Makefile for the VirtualBox FreeBSD Host Driver. +# + +# +# Copyright (C) 2006-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 . +# +# 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 +# + +KMOD = vboxnetadp + +CFLAGS += -DRT_OS_FREEBSD -DIN_RING0 -DIN_RT_R0 -DIN_SUP_R0 -DVBOX -DRT_WITH_VBOX -Iinclude -I. -Ir0drv -w -DVBOX_WITH_HARDENING -DVIMAGE + +.if (${MACHINE_ARCH} == "i386") + CFLAGS += -DRT_ARCH_X86 +.elif (${MACHINE_ARCH} == "amd64") + CFLAGS += -DRT_ARCH_AMD64 +.endif + +SRCS = \ + VBoxNetAdp-freebsd.c \ + VBoxNetAdp.c + +SRCS += device_if.h bus_if.h + +.include + diff --git a/src/VBox/HostDrivers/VBoxNetAdp/freebsd/VBoxNetAdp-freebsd.c b/src/VBox/HostDrivers/VBoxNetAdp/freebsd/VBoxNetAdp-freebsd.c new file mode 100644 index 00000000..db5c0b64 --- /dev/null +++ b/src/VBox/HostDrivers/VBoxNetAdp/freebsd/VBoxNetAdp-freebsd.c @@ -0,0 +1,339 @@ +/* $Id: VBoxNetAdp-freebsd.c $ */ +/** @file + * VBoxNetAdp - Virtual Network Adapter Driver (Host), FreeBSD Specific Code. + */ + +/*- + * Copyright (c) 2009 Fredrik Lindberg + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include +#undef PVM +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#define LOG_GROUP LOG_GROUP_NET_ADP_DRV +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define VBOXNETADP_OS_SPECFIC 1 +#include "../VBoxNetAdpInternal.h" + +#if defined(__FreeBSD_version) && __FreeBSD_version >= 800500 +# include +# include + +# define VBOXCURVNET_SET(arg) CURVNET_SET_QUIET(arg) +# define VBOXCURVNET_SET_FROM_UCRED() VBOXCURVNET_SET(CRED_TO_VNET(curthread->td_ucred)) +# define VBOXCURVNET_RESTORE() CURVNET_RESTORE() + +#else /* !defined(__FreeBSD_version) || __FreeBSD_version < 800500 */ + +# define VBOXCURVNET_SET(arg) +# define VBOXCURVNET_SET_FROM_UCRED() +# define VBOXCURVNET_RESTORE() + +#endif /* !defined(__FreeBSD_version) || __FreeBSD_version < 800500 */ + +static int VBoxNetAdpFreeBSDCtrlioctl(struct cdev *, u_long, caddr_t, int flags, + struct thread *); +static struct cdevsw vboxnetadp_cdevsw = +{ + .d_version = D_VERSION, + .d_ioctl = VBoxNetAdpFreeBSDCtrlioctl, + .d_read = (d_read_t *)nullop, + .d_write = (d_write_t *)nullop, + .d_name = VBOXNETADP_CTL_DEV_NAME, +}; + +static struct cdev *VBoxNetAdpFreeBSDcdev; + +static int VBoxNetAdpFreeBSDModuleEvent(struct module *, int, void *); +static moduledata_t g_VBoxNetAdpFreeBSDModule = { + "vboxnetadp", + VBoxNetAdpFreeBSDModuleEvent, + NULL +}; + +/** Declare the module as a pseudo device. */ +DECLARE_MODULE(vboxnetadp, g_VBoxNetAdpFreeBSDModule, SI_SUB_PSEUDO, SI_ORDER_ANY); +MODULE_VERSION(vboxnetadp, 1); +MODULE_DEPEND(vboxnetadp, vboxdrv, 1, 1, 1); +MODULE_DEPEND(vboxnetadp, vboxnetflt, 1, 1, 1); + +/** + * Module event handler + */ +static int +VBoxNetAdpFreeBSDModuleEvent(struct module *pMod, int enmEventType, void *pvArg) +{ + int rc = 0; + + Log(("VBoxNetAdpFreeBSDModuleEvent\n")); + + switch (enmEventType) + { + case MOD_LOAD: + rc = RTR0Init(0); + if (RT_FAILURE(rc)) + { + Log(("RTR0Init failed %d\n", rc)); + return RTErrConvertToErrno(rc); + } + rc = vboxNetAdpInit(); + if (RT_FAILURE(rc)) + { + RTR0Term(); + Log(("vboxNetAdpInit failed %d\n", rc)); + return RTErrConvertToErrno(rc); + } + /* Create dev node */ + VBoxNetAdpFreeBSDcdev = make_dev(&vboxnetadp_cdevsw, 0, + UID_ROOT, GID_WHEEL, 0600, VBOXNETADP_CTL_DEV_NAME); + + break; + + case MOD_UNLOAD: + vboxNetAdpShutdown(); + destroy_dev(VBoxNetAdpFreeBSDcdev); + RTR0Term(); + break; + case MOD_SHUTDOWN: + case MOD_QUIESCE: + default: + return EOPNOTSUPP; + } + + if (RT_SUCCESS(rc)) + return 0; + return RTErrConvertToErrno(rc); +} + +/** + * Device I/O Control entry point. + */ +static int +VBoxNetAdpFreeBSDCtrlioctl(struct cdev *dev, u_long iCmd, caddr_t data, int flags, struct thread *td) +{ + PVBOXNETADP pAdp; + PVBOXNETADPREQ pReq = (PVBOXNETADPREQ)data; + struct ifnet *ifp; + int rc; + + switch (iCmd) + { + case VBOXNETADP_CTL_ADD: + if ( !(iCmd & IOC_INOUT) /* paranoia*/ + || IOCPARM_LEN(iCmd) < sizeof(*pReq)) + return EINVAL; + + rc = vboxNetAdpCreate(&pAdp, + pReq->szName[0] && RTStrEnd(pReq->szName, RT_MIN(IOCPARM_LEN(iCmd), sizeof(pReq->szName))) ? + pReq->szName : NULL); + if (RT_FAILURE(rc)) + return EINVAL; + + strncpy(pReq->szName, pAdp->szName, sizeof(pReq->szName) - 1); + pReq->szName[sizeof(pReq->szName) - 1] = '\0'; + break; + + case VBOXNETADP_CTL_REMOVE: + if (!RTStrEnd(pReq->szName, RT_MIN(sizeof(pReq->szName), IOCPARM_LEN(iCmd)))) + return EINVAL; + + pAdp = vboxNetAdpFindByName(pReq->szName); + if (!pAdp) + return EINVAL; + + rc = vboxNetAdpDestroy(pAdp); + if (RT_FAILURE(rc)) + return EINVAL; + + break; + + default: + return EINVAL; + } + return 0; +} + +/** + * Initialize device, just set the running flag. + */ +static void VBoxNetAdpFreeBSDNetinit(void *priv) +{ + PVBOXNETADP pThis = priv; + struct ifnet *ifp = pThis->u.s.ifp; + + ifp->if_drv_flags |= IFF_DRV_RUNNING; +} + +/** + * Transmit packets. + * netflt has already done everything for us so we just hand the + * packets to BPF and increment the packet stats. + */ +static void VBoxNetAdpFreeBSDNetstart(struct ifnet *ifp) +{ + PVBOXNETADP pThis = ifp->if_softc; + struct mbuf *m; + + if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) != IFF_DRV_RUNNING) + return; + + ifp->if_drv_flags |= IFF_DRV_OACTIVE; + while (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) + { +#if __FreeBSD_version >= 1100036 + if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); +#else + ifp->if_opackets++; +#endif + IFQ_DRV_DEQUEUE(&ifp->if_snd, m); + BPF_MTAP(ifp, m); + m_freem(m); + } + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; +} + +/** + * Interface ioctl handling + */ +static int VBoxNetAdpFreeBSDNetioctl(struct ifnet *ifp, u_long cmd, caddr_t data) +{ + int error = 0; + + switch (cmd) + { + case SIOCSIFFLAGS: + if (ifp->if_flags & IFF_UP) + { + if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) + ifp->if_init(ifp->if_softc); + } + else + { + if (ifp->if_drv_flags & IFF_DRV_RUNNING) + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; + } + break; + case SIOCGIFMEDIA: + { + struct ifmediareq *ifmr; + int count; + + ifmr = (struct ifmediareq *)data; + count = ifmr->ifm_count; + ifmr->ifm_count = 1; + ifmr->ifm_status = IFM_AVALID; + ifmr->ifm_active = IFM_ETHER; + ifmr->ifm_status |= IFM_ACTIVE; + ifmr->ifm_current = ifmr->ifm_active; + if (count >= 1) + { + int media = IFM_ETHER; + error = copyout(&media, ifmr->ifm_ulist, sizeof(int)); + } + break; + } + default: + return ether_ioctl(ifp, cmd, data); + } + return error; +} + +int vboxNetAdpOsInit(PVBOXNETADP pThis) +{ + pThis->u.s.ifp = NULL; + return VINF_SUCCESS;; +} + +int vboxNetAdpOsCreate(PVBOXNETADP pThis, PCRTMAC pMac) +{ + struct ifnet *ifp; + + VBOXCURVNET_SET_FROM_UCRED(); + ifp = if_alloc(IFT_ETHER); + if (ifp == NULL) + return VERR_NO_MEMORY; + + if_initname(ifp, VBOXNETADP_NAME, pThis->iUnit); + ifp->if_softc = pThis; + ifp->if_mtu = ETHERMTU; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_ioctl = VBoxNetAdpFreeBSDNetioctl; + ifp->if_start = VBoxNetAdpFreeBSDNetstart; + ifp->if_init = VBoxNetAdpFreeBSDNetinit; + IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); + ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; + IFQ_SET_READY(&ifp->if_snd); + ether_ifattach(ifp, (void *)pMac); + ifp->if_baudrate = 0; + + strncpy(pThis->szName, ifp->if_xname, VBOXNETADP_MAX_NAME_LEN); + pThis->u.s.ifp = ifp; + VBOXCURVNET_RESTORE(); + return 0; +} + +void vboxNetAdpOsDestroy(PVBOXNETADP pThis) +{ + struct ifnet *ifp; + + ifp = pThis->u.s.ifp; + VBOXCURVNET_SET(ifp->if_vnet); + ether_ifdetach(ifp); + if_free(ifp); + VBOXCURVNET_RESTORE(); +} diff --git a/src/VBox/HostDrivers/VBoxNetAdp/freebsd/files_vboxnetadp b/src/VBox/HostDrivers/VBoxNetAdp/freebsd/files_vboxnetadp new file mode 100755 index 00000000..d90ced7b --- /dev/null +++ b/src/VBox/HostDrivers/VBoxNetAdp/freebsd/files_vboxnetadp @@ -0,0 +1,94 @@ +#!/bin/sh +# $Id: files_vboxnetadp $ +## @file +# Shared file between Makefile.kmk and export_modules.sh. +# + +# +# Copyright (C) 2007-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 . +# +# 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 +# + + +VBOX_VBOXNETADP_SOURCES=" \ + ${PATH_ROOT}/include/iprt/alloc.h=>include/iprt/alloc.h \ + ${PATH_ROOT}/include/iprt/alloca.h=>include/iprt/alloca.h \ + ${PATH_ROOT}/include/iprt/asm.h=>include/iprt/asm.h \ + ${PATH_ROOT}/include/iprt/asm-amd64-x86.h=>include/iprt/asm-amd64-x86.h \ + ${PATH_ROOT}/include/iprt/asm-math.h=>include/iprt/asm-math.h \ + ${PATH_ROOT}/include/iprt/assert.h=>include/iprt/assert.h \ + ${PATH_ROOT}/include/iprt/assertcompile.h=>include/iprt/assertcompile.h \ + ${PATH_ROOT}/include/iprt/avl.h=>include/iprt/avl.h \ + ${PATH_ROOT}/include/iprt/cdefs.h=>include/iprt/cdefs.h \ + ${PATH_ROOT}/include/iprt/cpuset.h=>include/iprt/cpuset.h \ + ${PATH_ROOT}/include/iprt/ctype.h=>include/iprt/ctype.h \ + ${PATH_ROOT}/include/iprt/err.h=>include/iprt/err.h \ + ${PATH_ROOT}/include/iprt/errcore.h=>include/iprt/errcore.h \ + ${PATH_ROOT}/include/iprt/heap.h=>include/iprt/heap.h \ + ${PATH_ROOT}/include/iprt/initterm.h=>include/iprt/initterm.h \ + ${PATH_ROOT}/include/iprt/latin1.h=>include/iprt/latin1.h \ + ${PATH_ROOT}/include/iprt/log.h=>include/iprt/log.h \ + ${PATH_ROOT}/include/iprt/mangling.h=>include/iprt/mangling.h \ + ${PATH_ROOT}/include/iprt/mem.h=>include/iprt/mem.h \ + ${PATH_ROOT}/include/iprt/memobj.h=>include/iprt/memobj.h \ + ${PATH_ROOT}/include/iprt/mp.h=>include/iprt/mp.h \ + ${PATH_ROOT}/include/iprt/param.h=>include/iprt/param.h \ + ${PATH_ROOT}/include/iprt/power.h=>include/iprt/power.h \ + ${PATH_ROOT}/include/iprt/process.h=>include/iprt/process.h \ + ${PATH_ROOT}/include/iprt/semaphore.h=>include/iprt/semaphore.h \ + ${PATH_ROOT}/include/iprt/spinlock.h=>include/iprt/spinlock.h \ + ${PATH_ROOT}/include/iprt/stdarg.h=>include/iprt/stdarg.h \ + ${PATH_ROOT}/include/iprt/stdint.h=>include/iprt/stdint.h \ + ${PATH_ROOT}/include/iprt/string.h=>include/iprt/string.h \ + ${PATH_ROOT}/include/iprt/thread.h=>include/iprt/thread.h \ + ${PATH_ROOT}/include/iprt/time.h=>include/iprt/time.h \ + ${PATH_ROOT}/include/iprt/timer.h=>include/iprt/timer.h \ + ${PATH_ROOT}/include/iprt/types.h=>include/iprt/types.h \ + ${PATH_ROOT}/include/iprt/uni.h=>include/iprt/uni.h \ + ${PATH_ROOT}/include/iprt/utf16.h=>include/iprt/utf16.h \ + ${PATH_ROOT}/include/iprt/uuid.h=>include/iprt/uuid.h \ + ${PATH_ROOT}/include/iprt/x86-helpers.h=>include/iprt/x86-helpers.h \ + ${PATH_ROOT}/include/iprt/nocrt/limits.h=>include/iprt/nocrt/limits.h \ + ${PATH_ROOT}/include/VBox/cdefs.h=>include/VBox/cdefs.h \ + ${PATH_ROOT}/include/VBox/err.h=>include/VBox/err.h \ + ${PATH_ROOT}/include/VBox/log.h=>include/VBox/log.h \ + ${PATH_ROOT}/include/VBox/intnet.h=>include/VBox/intnet.h \ + ${PATH_ROOT}/include/VBox/vmm/stam.h=>include/VBox/vmm/stam.h \ + ${PATH_ROOT}/include/VBox/sup.h=>include/VBox/sup.h \ + ${PATH_ROOT}/include/VBox/types.h=>include/VBox/types.h \ + ${PATH_ROOT}/include/VBox/version.h=>include/VBox/version.h \ + ${PATH_ROOT}/include/VBox/SUPDrvMangling.h=>include/VBox/SUPDrvMangling.h \ + ${PATH_ROOT}/src/VBox/HostDrivers/VBoxNetAdp/freebsd/VBoxNetAdp-freebsd.c=>VBoxNetAdp-freebsd.c \ + ${PATH_ROOT}/src/VBox/HostDrivers/VBoxNetAdp/VBoxNetAdp.c=>VBoxNetAdp.c \ + ${PATH_ROOT}/src/VBox/HostDrivers/VBoxNetAdp/VBoxNetAdpInternal.h=>VBoxNetAdpInternal.h \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/freebsd/the-freebsd-kernel.h=>r0drv/freebsd/the-freebsd-kernel.h \ + ${PATH_OUT}/version-generated.h=>version-generated.h \ + ${PATH_OUT}/product-generated.h=>product-generated.h \ +" + diff --git a/src/VBox/HostDrivers/VBoxNetAdp/linux/Makefile b/src/VBox/HostDrivers/VBoxNetAdp/linux/Makefile new file mode 100644 index 00000000..30aada92 --- /dev/null +++ b/src/VBox/HostDrivers/VBoxNetAdp/linux/Makefile @@ -0,0 +1,78 @@ +# $Id: Makefile $ +## @file +# Makefile for the VirtualBox Linux Host Virtual Network Adapter Driver. +# + +# +# Copyright (C) 2006-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 . +# +# 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 +# + +# Linux kbuild sets this to our source directory if we are called from there +obj ?= $(CURDIR) +include $(obj)/Makefile-header.gmk +VBOXNETADP_DIR := $(VBOX_MODULE_SRC_DIR) + +# Allow building directly from the subdirectory without assuming the toplevel +# makefile has done the copying. Not the default use case, but can be handy. +ifndef KBUILD_EXTRA_SYMBOLS +KBUILD_EXTRA_SYMBOLS=$(abspath $(VBOXNETADP_DIR)/../vboxdrv/Module.symvers) +endif + +VBOXMOD_NAME = vboxnetadp +VBOXMOD_OBJS = \ + linux/VBoxNetAdp-linux.o \ + VBoxNetAdp.o +ifeq ($(VBOX_KBUILD_TARGET_ARCH),x86) +VBOXMOD_OBJS += \ + math/gcc/divdi3.o \ + math/gcc/moddi3.o \ + math/gcc/qdivrem.o \ + math/gcc/udivdi3.o \ + math/gcc/udivmoddi4.o \ + math/gcc/divdi3.o \ + math/gcc/umoddi3.o +endif +VBOXMOD_INCL = \ + $(VBOXNETADP_DIR) \ + $(VBOXNETADP_DIR)include \ + $(VBOXNETADP_DIR)r0drv/linux +VBOXMOD_DEFS = \ + RT_OS_LINUX \ + IN_RING0 \ + IN_RT_R0 \ + IN_SUP_R0 \ + VBOX \ + RT_WITH_VBOX \ + VBOX_WITH_HARDENING \ + VBOX_WITH_64_BITS_GUESTS # <-- must be consistent with Config.kmk! +VBOXMOD_CFLAGS = -include $(VBOXNETADP_DIR)include/VBox/SUPDrvMangling.h -fno-pie -Wno-declaration-after-statement + +include $(obj)/Makefile-footer.gmk + diff --git a/src/VBox/HostDrivers/VBoxNetAdp/linux/Makefile.kup b/src/VBox/HostDrivers/VBoxNetAdp/linux/Makefile.kup new file mode 100644 index 00000000..e69de29b diff --git a/src/VBox/HostDrivers/VBoxNetAdp/linux/VBoxNetAdp-linux.c b/src/VBox/HostDrivers/VBoxNetAdp/linux/VBoxNetAdp-linux.c new file mode 100644 index 00000000..0654e1d7 --- /dev/null +++ b/src/VBox/HostDrivers/VBoxNetAdp/linux/VBoxNetAdp-linux.c @@ -0,0 +1,583 @@ +/* $Id: VBoxNetAdp-linux.c $ */ +/** @file + * VBoxNetAdp - Virtual Network Adapter Driver (Host), Linux Specific Code. + */ + +/* + * Copyright (C) 2009-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 . + * + * 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 * +*********************************************************************************************************************************/ +#include "the-linux-kernel.h" +#include "version-generated.h" +#include "revision-generated.h" +#include "product-generated.h" +#include +#include +#include +#include + +#define LOG_GROUP LOG_GROUP_NET_ADP_DRV +#include +#include +#include +#include +#include +#include + +/* +#include +#include +#include +#include +#include +#include +*/ + +#define VBOXNETADP_OS_SPECFIC 1 +#include "../VBoxNetAdpInternal.h" + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +#define VBOXNETADP_LINUX_NAME "vboxnet%d" +#define VBOXNETADP_CTL_DEV_NAME "vboxnetctl" + +#define VBOXNETADP_FROM_IFACE(iface) ((PVBOXNETADP) ifnet_softc(iface)) + +/** Set netdev MAC address. */ +#if RTLNX_VER_MIN(5,17,0) +# define VBOX_DEV_ADDR_SET(dev, addr, len) dev_addr_mod(dev, 0, addr, len) +#else /* < 5.17.0 */ +# define VBOX_DEV_ADDR_SET(dev, addr, len) memcpy(dev->dev_addr, addr, len) +#endif + + +/********************************************************************************************************************************* +* Internal Functions * +*********************************************************************************************************************************/ +static int __init VBoxNetAdpLinuxInit(void); +static void __exit VBoxNetAdpLinuxUnload(void); + +static int VBoxNetAdpLinuxOpen(struct inode *pInode, struct file *pFilp); +static int VBoxNetAdpLinuxClose(struct inode *pInode, struct file *pFilp); +#if RTLNX_VER_MAX(2,6,36) +static int VBoxNetAdpLinuxIOCtl(struct inode *pInode, struct file *pFilp, + unsigned int uCmd, unsigned long ulArg); +#else /* >= 2,6,36 */ +static long VBoxNetAdpLinuxIOCtlUnlocked(struct file *pFilp, + unsigned int uCmd, unsigned long ulArg); +#endif /* >= 2,6,36 */ + +static void vboxNetAdpEthGetDrvinfo(struct net_device *dev, struct ethtool_drvinfo *info); +#if RTLNX_VER_MIN(4,20,0) +static int vboxNetAdpEthGetLinkSettings(struct net_device *pNetDev, struct ethtool_link_ksettings *pLinkSettings); +#else /* < 4,20,0 */ +static int vboxNetAdpEthGetSettings(struct net_device *dev, struct ethtool_cmd *cmd); +#endif /* < 4,20,0 */ + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +module_init(VBoxNetAdpLinuxInit); +module_exit(VBoxNetAdpLinuxUnload); + +MODULE_AUTHOR(VBOX_VENDOR); +MODULE_DESCRIPTION(VBOX_PRODUCT " Network Adapter Driver"); +MODULE_LICENSE("GPL"); +#ifdef MODULE_VERSION +MODULE_VERSION(VBOX_VERSION_STRING " r" RT_XSTR(VBOX_SVN_REV) " (" RT_XSTR(INTNETTRUNKIFPORT_VERSION) ")"); +#endif + +/** + * The (common) global data. + */ +static struct file_operations gFileOpsVBoxNetAdp = +{ + owner: THIS_MODULE, + open: VBoxNetAdpLinuxOpen, + release: VBoxNetAdpLinuxClose, +#if RTLNX_VER_MAX(2,6,36) + ioctl: VBoxNetAdpLinuxIOCtl, +#else /* RTLNX_VER_MIN(2,6,36) */ + unlocked_ioctl: VBoxNetAdpLinuxIOCtlUnlocked, +#endif /* RTLNX_VER_MIN(2,6,36) */ +}; + +/** The miscdevice structure. */ +static struct miscdevice g_CtlDev = +{ + minor: MISC_DYNAMIC_MINOR, + name: VBOXNETADP_CTL_DEV_NAME, + fops: &gFileOpsVBoxNetAdp, +# if RTLNX_VER_MAX(2,6,18) + devfs_name: VBOXNETADP_CTL_DEV_NAME +# endif +}; + +# if RTLNX_VER_MIN(2,6,19) +static const struct ethtool_ops gEthToolOpsVBoxNetAdp = +# else +static struct ethtool_ops gEthToolOpsVBoxNetAdp = +# endif +{ + .get_drvinfo = vboxNetAdpEthGetDrvinfo, +# if RTLNX_VER_MIN(4,20,0) + .get_link_ksettings = vboxNetAdpEthGetLinkSettings, +# else + .get_settings = vboxNetAdpEthGetSettings, +# endif + .get_link = ethtool_op_get_link, +}; + + +struct VBoxNetAdpPriv +{ + struct net_device_stats Stats; +}; + +typedef struct VBoxNetAdpPriv VBOXNETADPPRIV; +typedef VBOXNETADPPRIV *PVBOXNETADPPRIV; + +static int vboxNetAdpLinuxOpen(struct net_device *pNetDev) +{ + netif_start_queue(pNetDev); + return 0; +} + +static int vboxNetAdpLinuxStop(struct net_device *pNetDev) +{ + netif_stop_queue(pNetDev); + return 0; +} + +static int vboxNetAdpLinuxXmit(struct sk_buff *pSkb, struct net_device *pNetDev) +{ + PVBOXNETADPPRIV pPriv = netdev_priv(pNetDev); + + /* Update the stats. */ + pPriv->Stats.tx_packets++; + pPriv->Stats.tx_bytes += pSkb->len; +#if RTLNX_VER_MAX(2,6,31) + /* Update transmission time stamp. */ + pNetDev->trans_start = jiffies; +#endif + /* Nothing else to do, just free the sk_buff. */ + dev_kfree_skb(pSkb); + return 0; +} + +static struct net_device_stats *vboxNetAdpLinuxGetStats(struct net_device *pNetDev) +{ + PVBOXNETADPPRIV pPriv = netdev_priv(pNetDev); + return &pPriv->Stats; +} + + +/* ethtool_ops::get_drvinfo */ +static void vboxNetAdpEthGetDrvinfo(struct net_device *pNetDev, struct ethtool_drvinfo *info) +{ + PVBOXNETADPPRIV pPriv = netdev_priv(pNetDev); + NOREF(pPriv); + + RTStrPrintf(info->driver, sizeof(info->driver), + "%s", VBOXNETADP_NAME); + + /* + * Would be nice to include VBOX_SVN_REV, but it's not available + * here. Use file's svn revision via svn keyword? + */ + RTStrPrintf(info->version, sizeof(info->version), + "%s", VBOX_VERSION_STRING); + + RTStrPrintf(info->fw_version, sizeof(info->fw_version), + "0x%08X", INTNETTRUNKIFPORT_VERSION); + + RTStrPrintf(info->bus_info, sizeof(info->driver), + "N/A"); +} + + +# if RTLNX_VER_MIN(4,20,0) +/* ethtool_ops::get_link_ksettings */ +static int vboxNetAdpEthGetLinkSettings(struct net_device *pNetDev, struct ethtool_link_ksettings *pLinkSettings) +{ + /* We just need to set field we care for, the rest is done by ethtool_get_link_ksettings() helper in ethtool. */ + ethtool_link_ksettings_zero_link_mode(pLinkSettings, supported); + ethtool_link_ksettings_zero_link_mode(pLinkSettings, advertising); + ethtool_link_ksettings_zero_link_mode(pLinkSettings, lp_advertising); + pLinkSettings->base.speed = SPEED_10; + pLinkSettings->base.duplex = DUPLEX_FULL; + pLinkSettings->base.port = PORT_TP; + pLinkSettings->base.phy_address = 0; + pLinkSettings->base.transceiver = XCVR_INTERNAL; + pLinkSettings->base.autoneg = AUTONEG_DISABLE; + return 0; +} +#else /* RTLNX_VER_MAX(4,20,0) */ +/* ethtool_ops::get_settings */ +static int vboxNetAdpEthGetSettings(struct net_device *pNetDev, struct ethtool_cmd *cmd) +{ + cmd->supported = 0; + cmd->advertising = 0; +#if RTLNX_VER_MIN(2,6,27) + ethtool_cmd_speed_set(cmd, SPEED_10); +#else + cmd->speed = SPEED_10; +#endif + cmd->duplex = DUPLEX_FULL; + cmd->port = PORT_TP; + cmd->phy_address = 0; + cmd->transceiver = XCVR_INTERNAL; + cmd->autoneg = AUTONEG_DISABLE; + cmd->maxtxpkt = 0; + cmd->maxrxpkt = 0; + return 0; +} +#endif /* RTLNX_VER_MAX(4,20,0) */ + + +#if RTLNX_VER_MIN(2,6,29) +static const struct net_device_ops vboxNetAdpNetdevOps = { + .ndo_open = vboxNetAdpLinuxOpen, + .ndo_stop = vboxNetAdpLinuxStop, + .ndo_start_xmit = vboxNetAdpLinuxXmit, + .ndo_get_stats = vboxNetAdpLinuxGetStats +}; +#endif + +static void vboxNetAdpNetDevInit(struct net_device *pNetDev) +{ + PVBOXNETADPPRIV pPriv; + + ether_setup(pNetDev); +#if RTLNX_VER_MIN(2,6,29) + pNetDev->netdev_ops = &vboxNetAdpNetdevOps; +#else /* RTLNX_VER_MAX(2,6,29) */ + pNetDev->open = vboxNetAdpLinuxOpen; + pNetDev->stop = vboxNetAdpLinuxStop; + pNetDev->hard_start_xmit = vboxNetAdpLinuxXmit; + pNetDev->get_stats = vboxNetAdpLinuxGetStats; +#endif /* RTLNX_VER_MAX(2,6,29) */ +#if RTLNX_VER_MIN(4,10,0) + pNetDev->max_mtu = 65536; + pNetDev->features = NETIF_F_TSO + | NETIF_F_TSO6 + | NETIF_F_TSO_ECN + | NETIF_F_SG + | NETIF_F_HW_CSUM; +#endif /* RTLNX_VER_MIN(4,10,0) */ + + pNetDev->ethtool_ops = &gEthToolOpsVBoxNetAdp; + + pPriv = netdev_priv(pNetDev); + memset(pPriv, 0, sizeof(*pPriv)); +} + + +int vboxNetAdpOsCreate(PVBOXNETADP pThis, PCRTMAC pMACAddress) +{ + int rc = VINF_SUCCESS; + struct net_device *pNetDev; + + /* No need for private data. */ + pNetDev = alloc_netdev(sizeof(VBOXNETADPPRIV), + pThis->szName[0] ? pThis->szName : VBOXNETADP_LINUX_NAME, +#if RTLNX_VER_MIN(3,17,0) + NET_NAME_UNKNOWN, +#endif + vboxNetAdpNetDevInit); + if (pNetDev) + { + int err; + + if (pNetDev->dev_addr) + { + VBOX_DEV_ADDR_SET(pNetDev, pMACAddress, ETH_ALEN); + Log2(("vboxNetAdpOsCreate: pNetDev->dev_addr = %.6Rhxd\n", pNetDev->dev_addr)); + + /* + * We treat presence of VBoxNetFlt filter as our "carrier", + * see vboxNetFltSetLinkState(). + * + * operstates.txt: "On device allocation, networking core + * sets the flags equivalent to netif_carrier_ok() and + * !netif_dormant()" - so turn carrier off here. + */ + netif_carrier_off(pNetDev); + + err = register_netdev(pNetDev); + if (!err) + { + strncpy(pThis->szName, pNetDev->name, sizeof(pThis->szName)); + pThis->szName[sizeof(pThis->szName) - 1] = '\0'; + pThis->u.s.pNetDev = pNetDev; + Log2(("vboxNetAdpOsCreate: pThis=%p pThis->szName = %p\n", pThis, pThis->szName)); + return VINF_SUCCESS; + } + } + else + { + LogRel(("VBoxNetAdp: failed to set MAC address (dev->dev_addr == NULL)\n")); + err = EFAULT; + } + free_netdev(pNetDev); + rc = RTErrConvertFromErrno(err); + } + return rc; +} + +void vboxNetAdpOsDestroy(PVBOXNETADP pThis) +{ + struct net_device *pNetDev = pThis->u.s.pNetDev; + AssertPtr(pThis->u.s.pNetDev); + + pThis->u.s.pNetDev = NULL; + unregister_netdev(pNetDev); + free_netdev(pNetDev); +} + +/** + * Device open. Called on open /dev/vboxnetctl + * + * @param pInode Pointer to inode info structure. + * @param pFilp Associated file pointer. + */ +static int VBoxNetAdpLinuxOpen(struct inode *pInode, struct file *pFilp) +{ + Log(("VBoxNetAdpLinuxOpen: pid=%d/%d %s\n", RTProcSelf(), current->pid, current->comm)); + +#ifdef VBOX_WITH_HARDENING + /* + * Only root is allowed to access the device, enforce it! + */ + if (!capable(CAP_SYS_ADMIN)) + { + Log(("VBoxNetAdpLinuxOpen: admin privileges required!\n")); + return -EPERM; + } +#endif + + return 0; +} + + +/** + * Close device. + * + * @param pInode Pointer to inode info structure. + * @param pFilp Associated file pointer. + */ +static int VBoxNetAdpLinuxClose(struct inode *pInode, struct file *pFilp) +{ + Log(("VBoxNetAdpLinuxClose: pid=%d/%d %s\n", + RTProcSelf(), current->pid, current->comm)); + pFilp->private_data = NULL; + return 0; +} + +/** + * Device I/O Control entry point. + * + * @param pFilp Associated file pointer. + * @param uCmd The function specified to ioctl(). + * @param ulArg The argument specified to ioctl(). + */ +#if RTLNX_VER_MAX(2,6,36) +static int VBoxNetAdpLinuxIOCtl(struct inode *pInode, struct file *pFilp, + unsigned int uCmd, unsigned long ulArg) +#else /* RTLNX_VER_MIN(2,6,36) */ +static long VBoxNetAdpLinuxIOCtlUnlocked(struct file *pFilp, + unsigned int uCmd, unsigned long ulArg) +#endif /* RTLNX_VER_MIN(2,6,36) */ +{ + VBOXNETADPREQ Req; + PVBOXNETADP pAdp; + int rc; + char *pszName = NULL; + + Log(("VBoxNetAdpLinuxIOCtl: param len %#x; uCmd=%#x; add=%#x\n", _IOC_SIZE(uCmd), uCmd, VBOXNETADP_CTL_ADD)); + if (RT_UNLIKELY(_IOC_SIZE(uCmd) != sizeof(Req))) /* paranoia */ + { + Log(("VBoxNetAdpLinuxIOCtl: bad ioctl sizeof(Req)=%#x _IOC_SIZE=%#x; uCmd=%#x.\n", sizeof(Req), _IOC_SIZE(uCmd), uCmd)); + return -EINVAL; + } + + switch (uCmd) + { + case VBOXNETADP_CTL_ADD: + Log(("VBoxNetAdpLinuxIOCtl: _IOC_DIR(uCmd)=%#x; IOC_OUT=%#x\n", _IOC_DIR(uCmd), IOC_OUT)); + if (RT_UNLIKELY(copy_from_user(&Req, (void *)ulArg, sizeof(Req)))) + { + Log(("VBoxNetAdpLinuxIOCtl: copy_from_user(,%#lx,) failed; uCmd=%#x.\n", ulArg, uCmd)); + return -EFAULT; + } + Log(("VBoxNetAdpLinuxIOCtl: Add %s\n", Req.szName)); + + if (Req.szName[0]) + { + pAdp = vboxNetAdpFindByName(Req.szName); + if (pAdp) + { + Log(("VBoxNetAdpLinuxIOCtl: '%s' already exists\n", Req.szName)); + return -EINVAL; + } + pszName = Req.szName; + } + rc = vboxNetAdpCreate(&pAdp, pszName); + if (RT_FAILURE(rc)) + { + Log(("VBoxNetAdpLinuxIOCtl: vboxNetAdpCreate -> %Rrc\n", rc)); + return -(rc == VERR_OUT_OF_RESOURCES ? ENOMEM : EINVAL); + } + + Assert(strlen(pAdp->szName) < sizeof(Req.szName)); + strncpy(Req.szName, pAdp->szName, sizeof(Req.szName) - 1); + Req.szName[sizeof(Req.szName) - 1] = '\0'; + + if (RT_UNLIKELY(copy_to_user((void *)ulArg, &Req, sizeof(Req)))) + { + /* this is really bad! */ + /** @todo remove the adapter again? */ + printk(KERN_ERR "VBoxNetAdpLinuxIOCtl: copy_to_user(%#lx,,%#zx); uCmd=%#x!\n", ulArg, sizeof(Req), uCmd); + return -EFAULT; + } + Log(("VBoxNetAdpLinuxIOCtl: Successfully added '%s'\n", Req.szName)); + break; + + case VBOXNETADP_CTL_REMOVE: + if (RT_UNLIKELY(copy_from_user(&Req, (void *)ulArg, sizeof(Req)))) + { + Log(("VBoxNetAdpLinuxIOCtl: copy_from_user(,%#lx,) failed; uCmd=%#x.\n", ulArg, uCmd)); + return -EFAULT; + } + Log(("VBoxNetAdpLinuxIOCtl: Remove %s\n", Req.szName)); + + pAdp = vboxNetAdpFindByName(Req.szName); + if (!pAdp) + { + Log(("VBoxNetAdpLinuxIOCtl: '%s' not found\n", Req.szName)); + return -EINVAL; + } + + rc = vboxNetAdpDestroy(pAdp); + if (RT_FAILURE(rc)) + { + Log(("VBoxNetAdpLinuxIOCtl: vboxNetAdpDestroy('%s') -> %Rrc\n", Req.szName, rc)); + return -EINVAL; + } + Log(("VBoxNetAdpLinuxIOCtl: Successfully removed '%s'\n", Req.szName)); + break; + + default: + printk(KERN_ERR "VBoxNetAdpLinuxIOCtl: unknown command %x.\n", uCmd); + return -EINVAL; + } + + return 0; +} + +int vboxNetAdpOsInit(PVBOXNETADP pThis) +{ + /* + * Init linux-specific members. + */ + pThis->u.s.pNetDev = NULL; + + return VINF_SUCCESS; +} + + + +/** + * Initialize module. + * + * @returns appropriate status code. + */ +static int __init VBoxNetAdpLinuxInit(void) +{ + int rc; + /* + * Initialize IPRT. + */ + rc = RTR0Init(0); + if (RT_SUCCESS(rc)) + { + Log(("VBoxNetAdpLinuxInit\n")); + + rc = vboxNetAdpInit(); + if (RT_SUCCESS(rc)) + { + rc = misc_register(&g_CtlDev); + if (rc) + { + printk(KERN_ERR "VBoxNetAdp: Can't register " VBOXNETADP_CTL_DEV_NAME " device! rc=%d\n", rc); + return rc; + } + LogRel(("VBoxNetAdp: Successfully started.\n")); + return 0; + } + else + LogRel(("VBoxNetAdp: failed to register vboxnet0 device (rc=%d)\n", rc)); + } + else + LogRel(("VBoxNetAdp: failed to initialize IPRT (rc=%d)\n", rc)); + + return -RTErrConvertToErrno(rc); +} + + +/** + * Unload the module. + * + * @todo We have to prevent this if we're busy! + */ +static void __exit VBoxNetAdpLinuxUnload(void) +{ + Log(("VBoxNetAdpLinuxUnload\n")); + + /* + * Undo the work done during start (in reverse order). + */ + + vboxNetAdpShutdown(); + /* Remove control device */ + misc_deregister(&g_CtlDev); + + RTR0Term(); + + Log(("VBoxNetAdpLinuxUnload - done\n")); +} + diff --git a/src/VBox/HostDrivers/VBoxNetAdp/linux/files_vboxnetadp b/src/VBox/HostDrivers/VBoxNetAdp/linux/files_vboxnetadp new file mode 100755 index 00000000..3f028677 --- /dev/null +++ b/src/VBox/HostDrivers/VBoxNetAdp/linux/files_vboxnetadp @@ -0,0 +1,112 @@ +#!/bin/sh +# $Id: files_vboxnetadp $ +## @files +# Shared file between Makefile.kmk and export_modules.sh. +# + +# +# Copyright (C) 2009-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 . +# +# 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 +# + +VBOX_VBOXNETADP_SOURCES=" \ + ${PATH_ROOT}/include/iprt/alloc.h=>include/iprt/alloc.h \ + ${PATH_ROOT}/include/iprt/alloca.h=>include/iprt/alloca.h \ + ${PATH_ROOT}/include/iprt/asm.h=>include/iprt/asm.h \ + ${PATH_ROOT}/include/iprt/asm-amd64-x86.h=>include/iprt/asm-amd64-x86.h \ + ${PATH_ROOT}/include/iprt/asm-math.h=>include/iprt/asm-math.h \ + ${PATH_ROOT}/include/iprt/assert.h=>include/iprt/assert.h \ + ${PATH_ROOT}/include/iprt/assertcompile.h=>include/iprt/assertcompile.h \ + ${PATH_ROOT}/include/iprt/avl.h=>include/iprt/avl.h \ + ${PATH_ROOT}/include/iprt/cdefs.h=>include/iprt/cdefs.h \ + ${PATH_ROOT}/include/iprt/cpuset.h=>include/iprt/cpuset.h \ + ${PATH_ROOT}/include/iprt/ctype.h=>include/iprt/ctype.h \ + ${PATH_ROOT}/include/iprt/err.h=>include/iprt/err.h \ + ${PATH_ROOT}/include/iprt/errcore.h=>include/iprt/errcore.h \ + ${PATH_ROOT}/include/iprt/heap.h=>include/iprt/heap.h \ + ${PATH_ROOT}/include/iprt/initterm.h=>include/iprt/initterm.h \ + ${PATH_ROOT}/include/iprt/latin1.h=>include/iprt/latin1.h \ + ${PATH_ROOT}/include/iprt/log.h=>include/iprt/log.h \ + ${PATH_ROOT}/include/iprt/mangling.h=>include/iprt/mangling.h \ + ${PATH_ROOT}/include/iprt/mem.h=>include/iprt/mem.h \ + ${PATH_ROOT}/include/iprt/memobj.h=>include/iprt/memobj.h \ + ${PATH_ROOT}/include/iprt/mp.h=>include/iprt/mp.h \ + ${PATH_ROOT}/include/iprt/net.h=>include/iprt/net.h \ + ${PATH_ROOT}/include/iprt/param.h=>include/iprt/param.h \ + ${PATH_ROOT}/include/iprt/power.h=>include/iprt/power.h \ + ${PATH_ROOT}/include/iprt/process.h=>include/iprt/process.h \ + ${PATH_ROOT}/include/iprt/semaphore.h=>include/iprt/semaphore.h \ + ${PATH_ROOT}/include/iprt/spinlock.h=>include/iprt/spinlock.h \ + ${PATH_ROOT}/include/iprt/stdarg.h=>include/iprt/stdarg.h \ + ${PATH_ROOT}/include/iprt/stdint.h=>include/iprt/stdint.h \ + ${PATH_ROOT}/include/iprt/string.h=>include/iprt/string.h \ + ${PATH_ROOT}/include/iprt/thread.h=>include/iprt/thread.h \ + ${PATH_ROOT}/include/iprt/time.h=>include/iprt/time.h \ + ${PATH_ROOT}/include/iprt/timer.h=>include/iprt/timer.h \ + ${PATH_ROOT}/include/iprt/types.h=>include/iprt/types.h \ + ${PATH_ROOT}/include/iprt/uint64.h=>include/iprt/uint64.h \ + ${PATH_ROOT}/include/iprt/uni.h=>include/iprt/uni.h \ + ${PATH_ROOT}/include/iprt/utf16.h=>include/iprt/utf16.h \ + ${PATH_ROOT}/include/iprt/uuid.h=>include/iprt/uuid.h \ + ${PATH_ROOT}/include/iprt/x86.h=>include/iprt/x86.h \ + ${PATH_ROOT}/include/iprt/x86-helpers.h=>include/iprt/x86-helpers.h \ + ${PATH_ROOT}/include/iprt/linux/version.h=>include/iprt/linux/version.h \ + ${PATH_ROOT}/include/iprt/nocrt/limits.h=>include/iprt/nocrt/limits.h \ + ${PATH_ROOT}/include/VBox/cdefs.h=>include/VBox/cdefs.h \ + ${PATH_ROOT}/include/VBox/err.h=>include/VBox/err.h \ + ${PATH_ROOT}/include/VBox/log.h=>include/VBox/log.h \ + ${PATH_ROOT}/include/VBox/intnet.h=>include/VBox/intnet.h \ + ${PATH_ROOT}/include/VBox/vmm/stam.h=>include/VBox/vmm/stam.h \ + ${PATH_ROOT}/include/VBox/sup.h=>include/VBox/sup.h \ + ${PATH_ROOT}/include/VBox/types.h=>include/VBox/types.h \ + ${PATH_ROOT}/include/VBox/SUPDrvMangling.h=>include/VBox/SUPDrvMangling.h \ + ${PATH_ROOT}/src/VBox/HostDrivers/VBoxNetAdp/linux/VBoxNetAdp-linux.c=>linux/VBoxNetAdp-linux.c \ + ${PATH_ROOT}/src/VBox/HostDrivers/VBoxNetAdp/VBoxNetAdp.c=>VBoxNetAdp.c \ + ${PATH_ROOT}/src/VBox/HostDrivers/VBoxNetAdp/VBoxNetAdpInternal.h=>VBoxNetAdpInternal.h \ + ${PATH_ROOT}/src/VBox/Installer/linux/Makefile-footer.gmk=>Makefile-footer.gmk \ + ${PATH_ROOT}/src/VBox/Installer/linux/Makefile-header.gmk=>Makefile-header.gmk \ + ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/divdi3.c=>math/gcc/divdi3.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/moddi3.c=>math/gcc/moddi3.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/qdivrem.c=>math/gcc/qdivrem.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/quad.h=>math/gcc/quad.h \ + ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/udivdi3.c=>math/gcc/udivdi3.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/udivmoddi4.c=>math/gcc/udivmoddi4.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/umoddi3.c=>math/gcc/umoddi3.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/string/strformat.cpp=>common/string/strformat.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/string/strformatrt.cpp=>common/string/strformatrt.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/string/strformattype.cpp=>common/string/strformattype.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/string/strprintf.cpp=>common/string/strprintf.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/string/strtonum.cpp=>common/string/strtonum.c \ + ${PATH_ROOT}/src/VBox/Runtime/include/internal/iprt.h=>include/internal/iprt.h \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/linux/the-linux-kernel.h=>r0drv/linux/the-linux-kernel.h \ + ${PATH_OUT}/version-generated.h=>version-generated.h \ + ${PATH_OUT}/revision-generated.h=>revision-generated.h \ + ${PATH_OUT}/product-generated.h=>product-generated.h \ +" + diff --git a/src/VBox/HostDrivers/VBoxNetAdp/solaris/VBoxNetAdp-solaris.c b/src/VBox/HostDrivers/VBoxNetAdp/solaris/VBoxNetAdp-solaris.c new file mode 100644 index 00000000..44227dbd --- /dev/null +++ b/src/VBox/HostDrivers/VBoxNetAdp/solaris/VBoxNetAdp-solaris.c @@ -0,0 +1,581 @@ +/* $Id: VBoxNetAdp-solaris.c $ */ +/** @file + * VBoxNetAdapter - Network Adapter Driver (Host), Solaris Specific Code. + */ + +/* + * Copyright (C) 2009-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 . + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../VBoxNetAdpInternal.h" + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +#define DEVICE_NAME "vboxnet" +/** The module descriptions as seen in 'modinfo'. */ +#define DEVICE_DESC_DRV "VirtualBox NetAdp" +/** The maximum MTU size permittable, value taken from "Oracle Quad 10 Gb or Dual 40 + * Gb Ethernet Adapter User's Guide". */ +#define DEVICE_MAX_MTU_SIZE 9706 + + +/********************************************************************************************************************************* +* Internal Functions * +*********************************************************************************************************************************/ +static int VBoxNetAdpSolarisAttach(dev_info_t *pDip, ddi_attach_cmd_t enmCmd); +static int VBoxNetAdpSolarisDetach(dev_info_t *pDip, ddi_detach_cmd_t enmCmd); +static int VBoxNetAdpSolarisQuiesceNotNeeded(dev_info_t *pDip); + +/** + * Streams: module info. + */ +static struct module_info g_VBoxNetAdpSolarisModInfo = +{ + 0x0dd, /* module id */ + DEVICE_NAME, + 0, /* min. packet size */ + INFPSZ, /* max. packet size */ + 0, /* hi-water mark */ + 0 /* lo-water mark */ +}; + +/** + * Streams: read queue hooks. + */ +static struct qinit g_VBoxNetAdpSolarisReadQ = +{ + NULL, /* read */ + gld_rsrv, + gld_open, + gld_close, + NULL, /* admin (reserved) */ + &g_VBoxNetAdpSolarisModInfo, + NULL /* module stats */ +}; + +/** + * Streams: write queue hooks. + */ +static struct qinit g_VBoxNetAdpSolarisWriteQ = +{ + gld_wput, + gld_wsrv, + NULL, /* open */ + NULL, /* close */ + NULL, /* admin (reserved) */ + &g_VBoxNetAdpSolarisModInfo, + NULL /* module stats */ +}; + +/** + * Streams: IO stream tab. + */ +static struct streamtab g_VBoxNetAdpSolarisStreamTab = +{ + &g_VBoxNetAdpSolarisReadQ, + &g_VBoxNetAdpSolarisWriteQ, + NULL, /* muxread init */ + NULL /* muxwrite init */ +}; + +/** + * cb_ops: driver char/block entry points + */ +static struct cb_ops g_VBoxNetAdpSolarisCbOps = +{ + nulldev, /* cb open */ + nulldev, /* cb close */ + nodev, /* b strategy */ + nodev, /* b dump */ + nodev, /* b print */ + nodev, /* cb read */ + nodev, /* cb write */ + nodev, /* cb ioctl */ + nodev, /* c devmap */ + nodev, /* c mmap */ + nodev, /* c segmap */ + nochpoll, /* c poll */ + ddi_prop_op, /* property ops */ + &g_VBoxNetAdpSolarisStreamTab, + D_MP, /* compat. flag */ + CB_REV /* revision */ +}; + +/** + * dev_ops: driver entry/exit and other ops. + */ +static struct dev_ops g_VBoxNetAdpSolarisDevOps = +{ + DEVO_REV, /* driver build revision */ + 0, /* ref count */ + gld_getinfo, + nulldev, /* identify */ + nulldev, /* probe */ + VBoxNetAdpSolarisAttach, + VBoxNetAdpSolarisDetach, + nodev, /* reset */ + &g_VBoxNetAdpSolarisCbOps, + (struct bus_ops *)0, + nodev, /* power */ + VBoxNetAdpSolarisQuiesceNotNeeded +}; + +/** + * modldrv: export driver specifics to kernel + */ +static struct modldrv g_VBoxNetAdpSolarisDriver = +{ + &mod_driverops, /* extern from kernel */ + DEVICE_DESC_DRV " " VBOX_VERSION_STRING "r" RT_XSTR(VBOX_SVN_REV), + &g_VBoxNetAdpSolarisDevOps +}; + +/** + * modlinkage: export install/remove/info to the kernel + */ +static struct modlinkage g_VBoxNetAdpSolarisModLinkage = +{ + MODREV_1, /* loadable module system revision */ + { + &g_VBoxNetAdpSolarisDriver, /* adapter streams driver framework */ + NULL /* terminate array of linkage structures */ + } +}; + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +/** The default ethernet broadcast address */ +static uchar_t achBroadcastAddr[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; + +/** + * vboxnetadp_state_t: per-instance data + */ +typedef struct vboxnetadp_state_t +{ + dev_info_t *pDip; /* device info. */ + RTMAC FactoryMac; /* default 'factory' MAC address */ + RTMAC CurrentMac; /* current MAC address */ +} vboxnetadp_state_t; + + +/********************************************************************************************************************************* +* Internal Functions * +*********************************************************************************************************************************/ +static int vboxNetAdpSolarisGenerateMac(PRTMAC pMac); +static int vboxNetAdpSolarisSetMacAddress(gld_mac_info_t *pMacInfo, unsigned char *pszMacAddr); +static int vboxNetAdpSolarisSend(gld_mac_info_t *pMacInfo, mblk_t *pMsg); +static int vboxNetAdpSolarisStub(gld_mac_info_t *pMacInfo); +static int vboxNetAdpSolarisSetPromisc(gld_mac_info_t *pMacInfo, int fPromisc); +static int vboxNetAdpSolarisSetMulticast(gld_mac_info_t *pMacInfo, unsigned char *pMulticastAddr, int fMulticast); +static int vboxNetAdpSolarisGetStats(gld_mac_info_t *pMacInfo, struct gld_stats *pStats); + + +/** + * Kernel entry points + */ +int _init(void) +{ + LogFunc((DEVICE_NAME ":_init\n")); + + /* + * Prevent module autounloading. + */ + modctl_t *pModCtl = mod_getctl(&g_VBoxNetAdpSolarisModLinkage); + if (pModCtl) + pModCtl->mod_loadflags |= MOD_NOAUTOUNLOAD; + else + LogRel((DEVICE_NAME ":failed to disable autounloading!\n")); + + /* + * Initialize IPRT. + */ + int rc = RTR0Init(0); + if (RT_SUCCESS(rc)) + { + rc = mod_install(&g_VBoxNetAdpSolarisModLinkage); + if (!rc) + return rc; + + LogRel((DEVICE_NAME ":mod_install failed. rc=%d\n", rc)); + RTR0Term(); + } + else + LogRel((DEVICE_NAME ":failed to initialize IPRT (rc=%d)\n", rc)); + + return RTErrConvertToErrno(rc); +} + + +int _fini(void) +{ + LogFunc((DEVICE_NAME ":_fini\n")); + + /* + * Undo the work done during start (in reverse order). + */ + int rc = mod_remove(&g_VBoxNetAdpSolarisModLinkage); + if (!rc) + RTR0Term(); + + return rc; +} + + +int _info(struct modinfo *pModInfo) +{ + LogFunc((DEVICE_NAME ":_info\n")); + + int rc = mod_info(&g_VBoxNetAdpSolarisModLinkage, pModInfo); + + Log((DEVICE_NAME ":_info returns %d\n", rc)); + return rc; +} + + +/** + * Attach entry point, to attach a device to the system or resume it. + * + * @param pDip The module structure instance. + * @param enmCmd Operation type (attach/resume). + * + * @returns corresponding solaris error code. + */ +static int VBoxNetAdpSolarisAttach(dev_info_t *pDip, ddi_attach_cmd_t enmCmd) +{ + LogFunc((DEVICE_NAME ":VBoxNetAdpSolarisAttach pDip=%p enmCmd=%d\n", pDip, enmCmd)); + + int rc = -1; + switch (enmCmd) + { + case DDI_ATTACH: + { + gld_mac_info_t *pMacInfo = gld_mac_alloc(pDip); + if (pMacInfo) + { + vboxnetadp_state_t *pState = RTMemAllocZ(sizeof(vboxnetadp_state_t)); + if (pState) + { + pState->pDip = pDip; + + /* + * Setup GLD MAC layer registration info. + */ + pMacInfo->gldm_reset = vboxNetAdpSolarisStub; + pMacInfo->gldm_start = vboxNetAdpSolarisStub; + pMacInfo->gldm_stop = vboxNetAdpSolarisStub; + pMacInfo->gldm_set_mac_addr = vboxNetAdpSolarisSetMacAddress; + pMacInfo->gldm_set_multicast = vboxNetAdpSolarisSetMulticast; + pMacInfo->gldm_set_promiscuous = vboxNetAdpSolarisSetPromisc; + pMacInfo->gldm_send = vboxNetAdpSolarisSend; + pMacInfo->gldm_intr = NULL; + pMacInfo->gldm_get_stats = vboxNetAdpSolarisGetStats; + pMacInfo->gldm_ioctl = NULL; + pMacInfo->gldm_ident = DEVICE_NAME; + pMacInfo->gldm_type = DL_ETHER; + pMacInfo->gldm_minpkt = 0; + pMacInfo->gldm_maxpkt = DEVICE_MAX_MTU_SIZE; + pMacInfo->gldm_capabilities = GLD_CAP_LINKSTATE; + AssertCompile(sizeof(RTMAC) == ETHERADDRL); + + pMacInfo->gldm_addrlen = ETHERADDRL; + pMacInfo->gldm_saplen = -2; + pMacInfo->gldm_broadcast_addr = achBroadcastAddr; + pMacInfo->gldm_ppa = ddi_get_instance(pState->pDip); + pMacInfo->gldm_devinfo = pState->pDip; + pMacInfo->gldm_private = (caddr_t)pState; + + /* + * We use a semi-random MAC addresses similar to a guest NIC's MAC address + * as the default factory address of the interface. + */ + rc = vboxNetAdpSolarisGenerateMac(&pState->FactoryMac); + if (RT_SUCCESS(rc)) + { + bcopy(&pState->FactoryMac, &pState->CurrentMac, sizeof(RTMAC)); + pMacInfo->gldm_vendor_addr = (unsigned char *)&pState->FactoryMac; + + /* + * Now try registering our GLD with the MAC layer. + * Registration can fail on some S10 versions when the MTU size is more than 1500. + * When we implement jumbo frames we should probably retry with MTU 1500 for S10. + */ + rc = gld_register(pDip, (char *)ddi_driver_name(pDip), pMacInfo); + if (rc == DDI_SUCCESS) + { + ddi_report_dev(pDip); + gld_linkstate(pMacInfo, GLD_LINKSTATE_UP); + return DDI_SUCCESS; + } + else + LogRel((DEVICE_NAME ":VBoxNetAdpSolarisAttach failed to register GLD. rc=%d\n", rc)); + } + else + LogRel((DEVICE_NAME ":VBoxNetAdpSolarisAttach failed to generate mac address.rc=%d\n")); + + RTMemFree(pState); + } + else + LogRel((DEVICE_NAME ":VBoxNetAdpSolarisAttach failed to alloc state.\n")); + + gld_mac_free(pMacInfo); + } + else + LogRel((DEVICE_NAME ":VBoxNetAdpSolarisAttach failed to alloc mac structure.\n")); + return DDI_FAILURE; + } + + case DDI_RESUME: + { + /* Nothing to do here... */ + return DDI_SUCCESS; + } + + /* case DDI_PM_RESUME: */ + default: + return DDI_FAILURE; + } +} + + +/** + * Detach entry point, to detach a device to the system or suspend it. + * + * @param pDip The module structure instance. + * @param enmCmd Operation type (detach/suspend). + * + * @returns corresponding solaris error code. + */ +static int VBoxNetAdpSolarisDetach(dev_info_t *pDip, ddi_detach_cmd_t enmCmd) +{ + LogFunc((DEVICE_NAME ":VBoxNetAdpSolarisDetach pDip=%p enmCmd=%d\n", pDip, enmCmd)); + + switch (enmCmd) + { + case DDI_DETACH: + { + /* + * Unregister and clean up. + */ + gld_mac_info_t *pMacInfo = ddi_get_driver_private(pDip); + if (pMacInfo) + { + vboxnetadp_state_t *pState = (vboxnetadp_state_t *)pMacInfo->gldm_private; + if (pState) + { + gld_linkstate(pMacInfo, GLD_LINKSTATE_DOWN); + int rc = gld_unregister(pMacInfo); + if (rc == DDI_SUCCESS) + { + gld_mac_free(pMacInfo); + RTMemFree(pState); + return DDI_SUCCESS; + } + else + LogRel((DEVICE_NAME ":VBoxNetAdpSolarisDetach failed to unregister GLD from MAC layer.rc=%d\n", rc)); + } + else + LogRel((DEVICE_NAME ":VBoxNetAdpSolarisDetach failed to get internal state.\n")); + } + else + LogRel((DEVICE_NAME ":VBoxNetAdpSolarisDetach failed to get driver private GLD data.\n")); + + return DDI_FAILURE; + } + + case DDI_RESUME: + { + /* Nothing to do here... */ + return DDI_SUCCESS; + } + + /* case DDI_SUSPEND: */ + /* case DDI_HOTPLUG_DETACH: */ + default: + return DDI_FAILURE; + } +} + + +/** + * Quiesce not-needed entry point, as Solaris 10 doesn't have any + * ddi_quiesce_not_needed() function. + * + * @param pDip The module structure instance. + * + * @return corresponding solaris error code. + */ +static int VBoxNetAdpSolarisQuiesceNotNeeded(dev_info_t *pDip) +{ + return DDI_SUCCESS; +} + + +static int vboxNetAdpSolarisGenerateMac(PRTMAC pMac) +{ + pMac->au8[0] = 0x08; + pMac->au8[1] = 0x00; + pMac->au8[2] = 0x27; + RTRandBytes(&pMac->au8[3], 3); + Log((DEVICE_NAME ":VBoxNetAdpSolarisGenerateMac Generated %.*Rhxs\n", sizeof(RTMAC), &pMac)); + return VINF_SUCCESS; +} + + +static int vboxNetAdpSolarisSetMacAddress(gld_mac_info_t *pMacInfo, unsigned char *pszMacAddr) +{ + vboxnetadp_state_t *pState = (vboxnetadp_state_t *)pMacInfo->gldm_private; + if (pState) + { + bcopy(pszMacAddr, &pState->CurrentMac, sizeof(RTMAC)); + Log((DEVICE_NAME ":vboxNetAdpSolarisSetMacAddress updated MAC %.*Rhxs\n", sizeof(RTMAC), &pState->CurrentMac)); + return GLD_SUCCESS; + } + else + LogRel((DEVICE_NAME ":vboxNetAdpSolarisSetMacAddress failed to get internal state.\n")); + return GLD_FAILURE; +} + + +static int vboxNetAdpSolarisSend(gld_mac_info_t *pMacInfo, mblk_t *pMsg) +{ + while (pMsg) + { + mblk_t *pMsgNext = pMsg->b_cont; + pMsg->b_cont = NULL; + freemsg(pMsg); + pMsg = pMsgNext; + } + return GLD_SUCCESS; +} + + +static int vboxNetAdpSolarisStub(gld_mac_info_t *pMacInfo) +{ + return GLD_SUCCESS; +} + + +static int vboxNetAdpSolarisSetMulticast(gld_mac_info_t *pMacInfo, unsigned char *pMulticastAddr, int fMulticast) +{ + return GLD_SUCCESS; +} + + +static int vboxNetAdpSolarisSetPromisc(gld_mac_info_t *pMacInfo, int fPromisc) +{ + /* Host requesting promiscuous intnet connection... */ + return GLD_SUCCESS; +} + + +static int vboxNetAdpSolarisGetStats(gld_mac_info_t *pMacInfo, struct gld_stats *pStats) +{ + /* + * For now fake up stats. Stats like duplex and speed are better set as they + * are used in utilities like dladm. Link state capabilities are critical + * as they are used by ipadm while trying to restore persistent interface configs. + */ + vboxnetadp_state_t *pState = (vboxnetadp_state_t *)pMacInfo->gldm_private; + if (pState) + { + pStats->glds_speed = 1000000000ULL; /* Bits/sec. */ + pStats->glds_media = GLDM_UNKNOWN; /* Media/Connector Type */ + pStats->glds_intr = 0; /* Interrupt count */ + pStats->glds_norcvbuf = 0; /* Recv. discards */ + pStats->glds_errxmt = 0; /* Xmit errors */ + pStats->glds_errrcv = 0; /* Recv. errors */ + pStats->glds_missed = 0; /* Pkt Drops on Recv. */ + pStats->glds_underflow = 0; /* Buffer underflows */ + pStats->glds_overflow = 0; /* Buffer overflows */ + + /* Ether */ + pStats->glds_frame = 0; /* Align errors */ + pStats->glds_crc = 0; /* CRC errors */ + pStats->glds_duplex = GLD_DUPLEX_FULL; /* Link duplex state */ + pStats->glds_nocarrier = 0; /* Carrier sense errors */ + pStats->glds_collisions = 0; /* Xmit Collisions */ + pStats->glds_excoll = 0; /* Frame discard due to excess collisions */ + pStats->glds_xmtlatecoll = 0; /* Late collisions */ + pStats->glds_defer = 0; /* Deferred Xmits */ + pStats->glds_dot3_first_coll = 0; /* Single collision frames */ + pStats->glds_dot3_multi_coll = 0; /* Multiple collision frames */ + pStats->glds_dot3_sqe_error = 0; /* SQE errors */ + pStats->glds_dot3_mac_xmt_error = 0; /* MAC Xmit errors */ + pStats->glds_dot3_mac_rcv_error = 0; /* Mac Recv. errors */ + pStats->glds_dot3_frame_too_long = 0; /* Frame too long errors */ + pStats->glds_short = 0; /* Runt frames */ + + pStats->glds_noxmtbuf = 0; /* Xmit Buf errors */ + pStats->glds_xmtretry = 0; /* Xmit retries */ + pStats->glds_multixmt = 0; /* Multicast Xmits */ + pStats->glds_multircv = 0; /* Multicast Recvs. */ + pStats->glds_brdcstxmt = 0; /* Broadcast Xmits*/ + pStats->glds_brdcstrcv = 0; /* Broadcast Recvs. */ + + return GLD_SUCCESS; + } + else + LogRel((DEVICE_NAME ":vboxNetAdpSolarisGetStats failed to get internal state.\n")); + return GLD_FAILURE; +} + diff --git a/src/VBox/HostDrivers/VBoxNetAdp/solaris/hostname.vboxnet0 b/src/VBox/HostDrivers/VBoxNetAdp/solaris/hostname.vboxnet0 new file mode 100644 index 00000000..30545bb9 --- /dev/null +++ b/src/VBox/HostDrivers/VBoxNetAdp/solaris/hostname.vboxnet0 @@ -0,0 +1 @@ +192.168.56.1 diff --git a/src/VBox/HostDrivers/VBoxNetAdp/solaris/vboxnet.conf b/src/VBox/HostDrivers/VBoxNetAdp/solaris/vboxnet.conf new file mode 100644 index 00000000..69aa69dd --- /dev/null +++ b/src/VBox/HostDrivers/VBoxNetAdp/solaris/vboxnet.conf @@ -0,0 +1,43 @@ +# $Id: vboxnet.conf $ +## @file +# Solaris Host VBoxNet Configuration +# + +# +# Copyright (C) 2009-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 . +# +# 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 +# + +# This needs to go into /platform/i86pc/kernel/drv, +# while the 64-bit driver object goes into the amd64 +# subdirectory (32-bit drivers goes into the same +# directory). +# +name="vboxnet" parent="pseudo" instance=0; + diff --git a/src/VBox/HostDrivers/VBoxNetAdp/win/Makefile.kup b/src/VBox/HostDrivers/VBoxNetAdp/win/Makefile.kup new file mode 100644 index 00000000..e69de29b diff --git a/src/VBox/HostDrivers/VBoxNetAdp/win/VBoxNetAdp-win.cpp b/src/VBox/HostDrivers/VBoxNetAdp/win/VBoxNetAdp-win.cpp new file mode 100644 index 00000000..d89ba4d0 --- /dev/null +++ b/src/VBox/HostDrivers/VBoxNetAdp/win/VBoxNetAdp-win.cpp @@ -0,0 +1,1888 @@ +/* $Id: VBoxNetAdp-win.cpp $ */ +/** @file + * VBoxNetAdp-win.cpp - NDIS6 Host-only Networking Driver, Windows-specific code. + */ +/* + * Copyright (C) 2014-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 . + * + * 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 + */ + +#define LOG_GROUP LOG_GROUP_NET_ADP_DRV + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "VBoxNetAdp-win.h" +#include "VBox/VBoxNetCmn-win.h" + +#define VBOXNETADP_MEM_TAG 'OHBV' + +/* + * By default the link speed reported to be 1Gbps. We may wish to lower + * it to 100Mbps to work around issues with multi-cast traffic on the host. + * See @bugref{6379}. + */ +#define VBOXNETADPWIN_LINK_SPEED 1000000000ULL + +#define LogError LogRel + +/* Forward declarations */ +MINIPORT_INITIALIZE vboxNetAdpWinInitializeEx; +MINIPORT_HALT vboxNetAdpWinHaltEx; +MINIPORT_UNLOAD vboxNetAdpWinUnload; +MINIPORT_PAUSE vboxNetAdpWinPause; +MINIPORT_RESTART vboxNetAdpWinRestart; +MINIPORT_OID_REQUEST vboxNetAdpWinOidRequest; +MINIPORT_SEND_NET_BUFFER_LISTS vboxNetAdpWinSendNetBufferLists; +MINIPORT_RETURN_NET_BUFFER_LISTS vboxNetAdpWinReturnNetBufferLists; +MINIPORT_CANCEL_SEND vboxNetAdpWinCancelSend; +MINIPORT_CHECK_FOR_HANG vboxNetAdpWinCheckForHangEx; +MINIPORT_RESET vboxNetAdpWinResetEx; +MINIPORT_DEVICE_PNP_EVENT_NOTIFY vboxNetAdpWinDevicePnPEventNotify; +MINIPORT_SHUTDOWN vboxNetAdpWinShutdownEx; +MINIPORT_CANCEL_OID_REQUEST vboxNetAdpWinCancelOidRequest; + + +/* Packet types by destination address; used in statistics. */ +typedef enum { + kVBoxNetAdpWinPacketType_Unicast, + kVBoxNetAdpWinPacketType_Multicast, + kVBoxNetAdpWinPacketType_Broadcast, + kVBoxNetAdpWinPacketType_ArraySize /* Must be the last one */ +} VBOXNETADPWIN_PACKET_TYPE; + + +/* Miniport states as defined by NDIS. */ +typedef enum { + kVBoxNetAdpWinState_Initializing, + kVBoxNetAdpWinState_Paused, + kVBoxNetAdpWinState_Restarting, + kVBoxNetAdpWinState_Running, + kVBoxNetAdpWinState_Pausing, + kVBoxNetAdpWinState_32BitHack = 0x7fffffff +} VBOXNETADPWIN_ADAPTER_STATE; + + +/* + * Valid state transitions are: + * 1) Disconnected -> Connecting : start the worker thread, attempting to init IDC; + * 2) Connecting -> Disconnected : failed to start IDC init worker thread; + * 3) Connecting -> Connected : IDC init successful, terminate the worker; + * 4) Connecting -> Stopping : IDC init incomplete, but the driver is being unloaded, terminate the worker; + * 5) Connected -> Stopping : IDC init was successful, no worker, the driver is being unloaded; + * + * Driver terminates in either in Disconnected or in Stopping state. + */ +typedef enum { + kVBoxNetAdpWinIdcState_Disconnected = 0, /* Initial state */ + kVBoxNetAdpWinIdcState_Connecting, /* Attemping to init IDC, worker thread running */ + kVBoxNetAdpWinIdcState_Connected, /* Successfully connected to IDC, worker thread terminated */ + kVBoxNetAdpWinIdcState_Stopping /* Terminating the worker thread and disconnecting IDC */ +} VBOXNETADPWIN_IDC_STATE; + +typedef struct _VBOXNETADPGLOBALS +{ + /** Miniport driver handle. */ + NDIS_HANDLE hMiniportDriver; + /** Power management capabilities, shared by all instances, do not change after init. */ + NDIS_PNP_CAPABILITIES PMCaps; + /** The INTNET trunk network interface factory. */ + INTNETTRUNKFACTORY TrunkFactory; + /** The SUPDRV component factory registration. */ + SUPDRVFACTORY SupDrvFactory; + /** The SUPDRV IDC handle (opaque struct). */ + SUPDRVIDCHANDLE SupDrvIDC; + /** IDC init thread handle. */ + HANDLE hInitIdcThread; + /** Lock protecting the following members. */ + NDIS_SPIN_LOCK Lock; + /** Lock-protected: the head of module list. */ + RTLISTANCHOR ListOfAdapters; + /** Lock-protected: The number of current factory references. */ + int32_t volatile cFactoryRefs; + /** Lock-protected: IDC initialization state. */ + volatile uint32_t enmIdcState; + /** Lock-protected: event signaled when trunk factory is not in use. */ + NDIS_EVENT EventUnloadAllowed; +} VBOXNETADPGLOBALS, *PVBOXNETADPGLOBALS; + +/* win-specific global data */ +VBOXNETADPGLOBALS g_VBoxNetAdpGlobals; + + +typedef struct _VBOXNETADP_ADAPTER { + /** Auxiliary member to link adapters into a list. */ + RTLISTNODE node; + /** Adapter handle for NDIS. */ + NDIS_HANDLE hAdapter; + /** Memory pool network buffers are allocated from. */ + NDIS_HANDLE hPool; + /** Our RJ-45 port. + * This is what the internal network plugs into. */ + INTNETTRUNKIFPORT MyPort; + /** The RJ-45 port on the INTNET "switch". + * This is what we're connected to. */ + PINTNETTRUNKSWPORT pSwitchPort; + /** Pointer to global data */ + PVBOXNETADPGLOBALS pGlobals; + /** Adapter state in NDIS, used for assertions only */ + VBOXNETADPWIN_ADAPTER_STATE volatile enmAdapterState; /// @todo do we need it really? + /** The trunk state. */ + INTNETTRUNKIFSTATE volatile enmTrunkState; + /** Number of pending operations, when it reaches zero we signal EventIdle. */ + int32_t volatile cBusy; + /** The event that is signaled when we go idle and that pfnWaitForIdle blocks on. */ + NDIS_EVENT EventIdle; + /** MAC address of adapter. */ + RTMAC MacAddr; + /** Statistics: bytes received from internal network. */ + uint64_t au64StatsInOctets[kVBoxNetAdpWinPacketType_ArraySize]; + /** Statistics: packets received from internal network. */ + uint64_t au64StatsInPackets[kVBoxNetAdpWinPacketType_ArraySize]; + /** Statistics: bytes sent to internal network. */ + uint64_t au64StatsOutOctets[kVBoxNetAdpWinPacketType_ArraySize]; + /** Statistics: packets sent to internal network. */ + uint64_t au64StatsOutPackets[kVBoxNetAdpWinPacketType_ArraySize]; + /** Adapter friendly name. */ + char szName[1]; +} VBOXNETADP_ADAPTER; +typedef VBOXNETADP_ADAPTER *PVBOXNETADP_ADAPTER; + + +/* Port */ + +#define IFPORT_2_VBOXNETADP_ADAPTER(pIfPort) \ + ( (PVBOXNETADP_ADAPTER)((uint8_t *)(pIfPort) - RT_UOFFSETOF(VBOXNETADP_ADAPTER, MyPort)) ) + +DECLINLINE(VBOXNETADPWIN_ADAPTER_STATE) vboxNetAdpWinGetState(PVBOXNETADP_ADAPTER pThis) +{ + return (VBOXNETADPWIN_ADAPTER_STATE)ASMAtomicUoReadU32((uint32_t volatile *)&pThis->enmAdapterState); +} + +DECLINLINE(VBOXNETADPWIN_ADAPTER_STATE) vboxNetAdpWinSetState(PVBOXNETADP_ADAPTER pThis, VBOXNETADPWIN_ADAPTER_STATE enmNewState) +{ + return (VBOXNETADPWIN_ADAPTER_STATE)ASMAtomicXchgU32((uint32_t volatile *)&pThis->enmAdapterState, enmNewState); +} + +DECLINLINE(bool) vboxNetAdpWinSetState(PVBOXNETADP_ADAPTER pThis, VBOXNETADPWIN_ADAPTER_STATE enmNewState, + VBOXNETADPWIN_ADAPTER_STATE enmOldState) +{ + return ASMAtomicCmpXchgU32((uint32_t volatile *)&pThis->enmAdapterState, enmNewState, enmOldState); +} + +#ifdef DEBUG + +DECLHIDDEN(void) vboxNetAdpWinDumpPackets(const char *pszMsg, PNET_BUFFER_LIST pBufLists) +{ + for (PNET_BUFFER_LIST pList = pBufLists; pList; pList = NET_BUFFER_LIST_NEXT_NBL(pList)) + { + for (PNET_BUFFER pBuf = NET_BUFFER_LIST_FIRST_NB(pList); pBuf; pBuf = NET_BUFFER_NEXT_NB(pBuf)) + { + Log6(("%s packet: cb=%d offset=%d", pszMsg, NET_BUFFER_DATA_LENGTH(pBuf), NET_BUFFER_DATA_OFFSET(pBuf))); + for (PMDL pMdl = NET_BUFFER_FIRST_MDL(pBuf); + pMdl != NULL; + pMdl = NDIS_MDL_LINKAGE(pMdl)) + { + Log6((" MDL: cb=%d", MmGetMdlByteCount(pMdl))); + } + Log6(("\n")); + } + } +} + +DECLINLINE(const char *) vboxNetAdpWinEthTypeStr(uint16_t uType) +{ + switch (uType) + { + case RTNET_ETHERTYPE_IPV4: return "IP"; + case RTNET_ETHERTYPE_IPV6: return "IPv6"; + case RTNET_ETHERTYPE_ARP: return "ARP"; + } + return "unknown"; +} + +#define VBOXNETADP_PKTDMPSIZE 0x50 + +/** + * Dump a packet to debug log. + * + * @param cpPacket The packet. + * @param cb The size of the packet. + * @param cszText A string denoting direction of packet transfer. + */ +DECLINLINE(void) vboxNetAdpWinDumpPacket(PCINTNETSG pSG, const char *cszText) +{ + uint8_t bPacket[VBOXNETADP_PKTDMPSIZE]; + + uint32_t cb = pSG->cbTotal < VBOXNETADP_PKTDMPSIZE ? pSG->cbTotal : VBOXNETADP_PKTDMPSIZE; + IntNetSgReadEx(pSG, 0, cb, bPacket); + + AssertReturnVoid(cb >= 14); + + uint8_t *pHdr = bPacket; + uint8_t *pEnd = bPacket + cb; + AssertReturnVoid(pEnd - pHdr >= 14); + uint16_t uEthType = RT_N2H_U16(*(uint16_t*)(pHdr+12)); + Log2(("NetADP: %s (%d bytes), %RTmac => %RTmac, EthType=%s(0x%x)\n", + cszText, pSG->cbTotal, pHdr+6, pHdr, vboxNetAdpWinEthTypeStr(uEthType), uEthType)); + pHdr += sizeof(RTNETETHERHDR); + if (uEthType == RTNET_ETHERTYPE_VLAN) + { + AssertReturnVoid(pEnd - pHdr >= 4); + uEthType = RT_N2H_U16(*(uint16_t*)(pHdr+2)); + Log2((" + VLAN: id=%d EthType=%s(0x%x)\n", RT_N2H_U16(*(uint16_t*)(pHdr)) & 0xFFF, + vboxNetAdpWinEthTypeStr(uEthType), uEthType)); + pHdr += 2 * sizeof(uint16_t); + } + uint8_t uProto = 0xFF; + switch (uEthType) + { + case RTNET_ETHERTYPE_IPV6: + AssertReturnVoid(pEnd - pHdr >= 40); + uProto = pHdr[6]; + Log2((" + IPv6: %RTnaipv6 => %RTnaipv6\n", pHdr+8, pHdr+24)); + pHdr += 40; + break; + case RTNET_ETHERTYPE_IPV4: + AssertReturnVoid(pEnd - pHdr >= 20); + uProto = pHdr[9]; + Log2((" + IP: %RTnaipv4 => %RTnaipv4\n", *(uint32_t*)(pHdr+12), *(uint32_t*)(pHdr+16))); + pHdr += (pHdr[0] & 0xF) * 4; + break; + case RTNET_ETHERTYPE_ARP: + AssertReturnVoid(pEnd - pHdr >= 28); + AssertReturnVoid(RT_N2H_U16(*(uint16_t*)(pHdr+2)) == RTNET_ETHERTYPE_IPV4); + switch (RT_N2H_U16(*(uint16_t*)(pHdr+6))) + { + case 1: /* ARP request */ + Log2((" + ARP-REQ: who-has %RTnaipv4 tell %RTnaipv4\n", + *(uint32_t*)(pHdr+24), *(uint32_t*)(pHdr+14))); + break; + case 2: /* ARP reply */ + Log2((" + ARP-RPL: %RTnaipv4 is-at %RTmac\n", + *(uint32_t*)(pHdr+14), pHdr+8)); + break; + default: + Log2((" + ARP: unknown op %d\n", RT_N2H_U16(*(uint16_t*)(pHdr+6)))); + break; + } + break; + /* There is no default case as uProto is initialized with 0xFF */ + } + while (uProto != 0xFF) + { + switch (uProto) + { + case 0: /* IPv6 Hop-by-Hop option*/ + case 60: /* IPv6 Destination option*/ + case 43: /* IPv6 Routing option */ + case 44: /* IPv6 Fragment option */ + Log2((" + IPv6 option (%d): \n", uProto)); + uProto = pHdr[0]; + pHdr += pHdr[1] * 8 + 8; /* Skip to the next extension/protocol */ + break; + case 51: /* IPv6 IPsec AH */ + Log2((" + IPv6 IPsec AH: \n")); + uProto = pHdr[0]; + pHdr += (pHdr[1] + 2) * 4; /* Skip to the next extension/protocol */ + break; + case 50: /* IPv6 IPsec ESP */ + /* Cannot decode IPsec, fall through */ + Log2((" + IPv6 IPsec ESP: \n")); + uProto = 0xFF; + break; + case 59: /* No Next Header */ + Log2((" + IPv6 No Next Header\n")); + uProto = 0xFF; + break; + case 58: /* IPv6-ICMP */ + switch (pHdr[0]) + { + case 1: Log2((" + IPv6-ICMP: destination unreachable, code %d\n", pHdr[1])); break; + case 128: Log2((" + IPv6-ICMP: echo request\n")); break; + case 129: Log2((" + IPv6-ICMP: echo reply\n")); break; + default: Log2((" + IPv6-ICMP: unknown type %d, code %d\n", pHdr[0], pHdr[1])); break; + } + uProto = 0xFF; + break; + case 1: /* ICMP */ + switch (pHdr[0]) + { + case 0: Log2((" + ICMP: echo reply\n")); break; + case 8: Log2((" + ICMP: echo request\n")); break; + case 3: Log2((" + ICMP: destination unreachable, code %d\n", pHdr[1])); break; + default: Log2((" + ICMP: unknown type %d, code %d\n", pHdr[0], pHdr[1])); break; + } + uProto = 0xFF; + break; + case 6: /* TCP */ + Log2((" + TCP: src=%d dst=%d seq=%x ack=%x\n", + RT_N2H_U16(*(uint16_t*)(pHdr)), RT_N2H_U16(*(uint16_t*)(pHdr+2)), + RT_N2H_U32(*(uint32_t*)(pHdr+4)), RT_N2H_U32(*(uint32_t*)(pHdr+8)))); + uProto = 0xFF; + break; + case 17: /* UDP */ + Log2((" + UDP: src=%d dst=%d\n", + RT_N2H_U16(*(uint16_t*)(pHdr)), RT_N2H_U16(*(uint16_t*)(pHdr+2)))); + uProto = 0xFF; + break; + default: + Log2((" + Unknown: proto=0x%x\n", uProto)); + uProto = 0xFF; + break; + } + } + Log3(("%.*Rhxd\n", cb, bPacket)); +} + +#else /* !DEBUG */ +//# define vboxNetAdpWinDumpFilterTypes(uFlags) do { } while (0) +//# define vboxNetAdpWinDumpOffloadSettings(p) do { } while (0) +//# define vboxNetAdpWinDumpSetOffloadSettings(p) do { } while (0) +# define vboxNetAdpWinDumpPackets(m,l) do { } while (0) +# define vboxNetAdpWinDumpPacket(p,t) do { } while (0) +#endif /* !DEBUG */ + + +DECLHIDDEN(VBOXNETADPWIN_PACKET_TYPE) vboxNetAdpWinPacketType(PINTNETSG pSG) +{ + static const uint8_t g_abBcastAddr[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; + AssertReturn(pSG->cbTotal >= sizeof(g_abBcastAddr), kVBoxNetAdpWinPacketType_Unicast); + AssertReturn(pSG->cSegsUsed > 0, kVBoxNetAdpWinPacketType_Unicast); + AssertReturn(pSG->aSegs[0].cb >= sizeof(g_abBcastAddr), kVBoxNetAdpWinPacketType_Unicast); + if (!memcmp(pSG->aSegs[0].pv, g_abBcastAddr, sizeof(g_abBcastAddr))) + return kVBoxNetAdpWinPacketType_Broadcast; + if ((*(uint8_t*)pSG->aSegs[0].pv) & 1) + return kVBoxNetAdpWinPacketType_Multicast; + return kVBoxNetAdpWinPacketType_Unicast; +} + +DECLINLINE(void) vboxNetAdpWinUpdateStats(uint64_t *pPacketStats, uint64_t *pOctetStats, PINTNETSG pSG) +{ + VBOXNETADPWIN_PACKET_TYPE enmPktType = vboxNetAdpWinPacketType(pSG); + ASMAtomicIncU64(&pPacketStats[enmPktType]); + ASMAtomicAddU64(&pOctetStats[enmPktType], pSG->cbTotal); +} + +DECLINLINE(void) vboxNetAdpWinFreeMdlChain(PMDL pMdl) +{ + PMDL pMdlNext; + while (pMdl) + { + pMdlNext = pMdl->Next; + PUCHAR pDataBuf; + ULONG cb = 0; + NdisQueryMdl(pMdl, &pDataBuf, &cb, NormalPagePriority); + NdisFreeMdl(pMdl); + Log4(("vboxNetAdpWinFreeMdlChain: freed MDL 0x%p\n", pMdl)); + NdisFreeMemory(pDataBuf, 0, 0); + Log4(("vboxNetAdpWinFreeMdlChain: freed data buffer 0x%p\n", pDataBuf)); + pMdl = pMdlNext; + } +} + +DECLHIDDEN(PNET_BUFFER_LIST) vboxNetAdpWinSGtoNB(PVBOXNETADP_ADAPTER pThis, PINTNETSG pSG) +{ + AssertReturn(pSG->cSegsUsed >= 1, NULL); + LogFlow(("==>vboxNetAdpWinSGtoNB: segments=%d hPool=%p cb=%u\n", pSG->cSegsUsed, + pThis->hPool, pSG->cbTotal)); + AssertReturn(pThis->hPool, NULL); + + + PNET_BUFFER_LIST pBufList = NULL; + ULONG cbMdl = pSG->cbTotal; + ULONG uDataOffset = cbMdl - pSG->cbTotal; + PUCHAR pDataBuf = (PUCHAR)NdisAllocateMemoryWithTagPriority(pThis->hAdapter, cbMdl, + VBOXNETADP_MEM_TAG, NormalPoolPriority); + if (pDataBuf) + { + Log4(("vboxNetAdpWinSGtoNB: allocated data buffer (cb=%u) 0x%p\n", cbMdl, pDataBuf)); + PMDL pMdl = NdisAllocateMdl(pThis->hAdapter, pDataBuf, cbMdl); + if (!pMdl) + { + NdisFreeMemory(pDataBuf, 0, 0); + Log4(("vboxNetAdpWinSGtoNB: freed data buffer 0x%p\n", pDataBuf)); + LogError(("vboxNetAdpWinSGtoNB: failed to allocate an MDL (cb=%u)\n", cbMdl)); + LogFlow(("<==vboxNetAdpWinSGtoNB: return NULL\n")); + return NULL; + } + PUCHAR pDst = pDataBuf + uDataOffset; + for (int i = 0; i < pSG->cSegsUsed; i++) + { + NdisMoveMemory(pDst, pSG->aSegs[i].pv, pSG->aSegs[i].cb); + pDst += pSG->aSegs[i].cb; + } + pBufList = NdisAllocateNetBufferAndNetBufferList(pThis->hPool, + 0 /* ContextSize */, + 0 /* ContextBackFill */, + pMdl, + uDataOffset, + pSG->cbTotal); + if (pBufList) + { + Log4(("vboxNetAdpWinSGtoNB: allocated NBL+NB 0x%p\n", pBufList)); + pBufList->SourceHandle = pThis->hAdapter; + /** @todo Do we need to initialize anything else? */ + } + else + { + LogError(("vboxNetAdpWinSGtoNB: failed to allocate an NBL+NB\n")); + vboxNetAdpWinFreeMdlChain(pMdl); + } + } + else + { + LogError(("vboxNetAdpWinSGtoNB: failed to allocate data buffer (size=%u)\n", cbMdl)); + } + + LogFlow(("<==vboxNetAdpWinSGtoNB: return %p\n", pBufList)); + return pBufList; +} + +DECLINLINE(void) vboxNetAdpWinDestroySG(PINTNETSG pSG) +{ + NdisFreeMemory(pSG, 0, 0); + Log4(("vboxNetAdpWinDestroySG: freed SG 0x%p\n", pSG)); +} + +/** + * Worker for vboxNetAdpWinNBtoSG() that gets the max segment count needed. + * @note vboxNetAdpWinNBtoSG may use fewer depending on cbPacket and offset! + * @note vboxNetLwfWinCalcSegments() is a copy of this code. + */ +DECLINLINE(ULONG) vboxNetAdpWinCalcSegments(PNET_BUFFER pNetBuf) +{ + ULONG cSegs = 0; + for (PMDL pMdl = NET_BUFFER_CURRENT_MDL(pNetBuf); pMdl; pMdl = NDIS_MDL_LINKAGE(pMdl)) + { + /* Skip empty MDLs (see @bugref{9233}) */ + if (MmGetMdlByteCount(pMdl)) + cSegs++; + } + return cSegs; +} + +/** + * @note vboxNetLwfWinNBtoSG() is a copy of this code. + */ +DECLHIDDEN(PINTNETSG) vboxNetAdpWinNBtoSG(PVBOXNETADP_ADAPTER pThis, PNET_BUFFER pNetBuf) +{ + ULONG cbPacket = NET_BUFFER_DATA_LENGTH(pNetBuf); + ULONG cSegs = vboxNetAdpWinCalcSegments(pNetBuf); + /* Allocate and initialize SG */ + PINTNETSG pSG = (PINTNETSG)NdisAllocateMemoryWithTagPriority(pThis->hAdapter, + RT_UOFFSETOF_DYN(INTNETSG, aSegs[cSegs]), + VBOXNETADP_MEM_TAG, + NormalPoolPriority); + AssertReturn(pSG, pSG); + Log4(("vboxNetAdpWinNBtoSG: allocated SG 0x%p\n", pSG)); + IntNetSgInitTempSegs(pSG, cbPacket /*cbTotal*/, cSegs, cSegs /*cSegsUsed*/); + + ULONG uOffset = NET_BUFFER_CURRENT_MDL_OFFSET(pNetBuf); + cSegs = 0; + for (PMDL pMdl = NET_BUFFER_CURRENT_MDL(pNetBuf); + pMdl != NULL && cbPacket > 0; + pMdl = NDIS_MDL_LINKAGE(pMdl)) + { + ULONG cbSrc = MmGetMdlByteCount(pMdl); + if (cbSrc == 0) + continue; /* Skip empty MDLs (see @bugref{9233}) */ + + PUCHAR pSrc = (PUCHAR)MmGetSystemAddressForMdlSafe(pMdl, LowPagePriority); + if (!pSrc) + { + vboxNetAdpWinDestroySG(pSG); + return NULL; + } + + /* Handle the offset in the current (which is the first for us) MDL */ + if (uOffset) + { + if (uOffset < cbSrc) + { + pSrc += uOffset; + cbSrc -= uOffset; + uOffset = 0; + } + else + { + /* This is an invalid MDL chain */ + vboxNetAdpWinDestroySG(pSG); + return NULL; + } + } + + /* Do not read the last MDL beyond packet's end */ + if (cbSrc > cbPacket) + cbSrc = cbPacket; + + Assert(cSegs < pSG->cSegsAlloc); + pSG->aSegs[cSegs].pv = pSrc; + pSG->aSegs[cSegs].cb = cbSrc; + pSG->aSegs[cSegs].Phys = NIL_RTHCPHYS; + cSegs++; + cbPacket -= cbSrc; + } + + Assert(cbPacket == 0); + Assert(cSegs <= pSG->cSegsUsed); + + /* Update actual segment count in case we used fewer than anticipated. */ + pSG->cSegsUsed = (uint16_t)cSegs; + + return pSG; +} + +DECLINLINE(bool) vboxNetAdpWinIsActive(PVBOXNETADP_ADAPTER pThis) +{ + if (vboxNetAdpWinGetState(pThis) != kVBoxNetAdpWinState_Running) + return false; + if (pThis->enmTrunkState != INTNETTRUNKIFSTATE_ACTIVE) + return false; + AssertPtrReturn(pThis->pSwitchPort, false); + return true; +} + +DECLHIDDEN(bool) vboxNetAdpWinForwardToIntNet(PVBOXNETADP_ADAPTER pThis, PNET_BUFFER_LIST pList, uint32_t fSrc) +{ + if (!vboxNetAdpWinIsActive(pThis)) + { + LogFlow(("vboxNetAdpWinForwardToIntNet: not active\n")); + return false; + } + AssertReturn(pThis->pSwitchPort, false); + AssertReturn(pThis->pSwitchPort->pfnRecv, false); + LogFlow(("==>vboxNetAdpWinForwardToIntNet\n")); + + if (ASMAtomicIncS32(&pThis->cBusy) == 1) + NdisResetEvent(&pThis->EventIdle); + for (PNET_BUFFER pBuf = NET_BUFFER_LIST_FIRST_NB(pList); pBuf; pBuf = NET_BUFFER_NEXT_NB(pBuf)) + { + PINTNETSG pSG = vboxNetAdpWinNBtoSG(pThis, pBuf); + if (pSG) + { + vboxNetAdpWinUpdateStats(pThis->au64StatsOutPackets, pThis->au64StatsOutOctets, pSG); + vboxNetAdpWinDumpPacket(pSG, (fSrc & INTNETTRUNKDIR_WIRE)?"intnet <-- wire":"intnet <-- host"); + pThis->pSwitchPort->pfnRecv(pThis->pSwitchPort, NULL, pSG, fSrc); + vboxNetAdpWinDestroySG(pSG); + } + } + if (ASMAtomicDecS32(&pThis->cBusy) == 0) + NdisSetEvent(&pThis->EventIdle); + + return true; +} + + +/** + * @copydoc INTNETTRUNKIFPORT::pfnRetain + */ +static DECLCALLBACK(void) vboxNetAdpWinPortRetain(PINTNETTRUNKIFPORT pIfPort) +{ + PVBOXNETADP_ADAPTER pThis = IFPORT_2_VBOXNETADP_ADAPTER(pIfPort); + RT_NOREF1(pThis); + LogFlow(("vboxNetAdpWinPortRetain: pThis=%p, pIfPort=%p\n", pThis, pIfPort)); +} + +/** + * @copydoc INTNETTRUNKIFPORT::pfnRelease + */ +static DECLCALLBACK(void) vboxNetAdpWinPortRelease(PINTNETTRUNKIFPORT pIfPort) +{ + PVBOXNETADP_ADAPTER pThis = IFPORT_2_VBOXNETADP_ADAPTER(pIfPort); + RT_NOREF1(pThis); + LogFlow(("vboxNetAdpWinPortRelease: pThis=%p, pIfPort=%p\n", pThis, pIfPort)); +} + +/** + * @copydoc INTNETTRUNKIFPORT::pfnDisconnectAndRelease + */ +static DECLCALLBACK(void) vboxNetAdpWinPortDisconnectAndRelease(PINTNETTRUNKIFPORT pIfPort) +{ + PVBOXNETADP_ADAPTER pThis = IFPORT_2_VBOXNETADP_ADAPTER(pIfPort); + + LogFlow(("vboxNetAdpWinPortDisconnectAndRelease: pThis=%p, pIfPort=%p\n", pThis, pIfPort)); + /* + * Serious paranoia. + */ + AssertPtr(pThis); + Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION); + Assert(pThis->MyPort.u32VersionEnd == INTNETTRUNKIFPORT_VERSION); + AssertPtr(pThis->pGlobals); + Assert(pThis->szName[0]); + + AssertPtr(pThis->pSwitchPort); + Assert(pThis->enmTrunkState == INTNETTRUNKIFSTATE_DISCONNECTING); + + pThis->pSwitchPort = NULL; +} + +/** + * @copydoc INTNETTRUNKIFPORT::pfnSetState + */ +static DECLCALLBACK(INTNETTRUNKIFSTATE) vboxNetAdpWinPortSetState(PINTNETTRUNKIFPORT pIfPort, INTNETTRUNKIFSTATE enmState) +{ + PVBOXNETADP_ADAPTER pThis = IFPORT_2_VBOXNETADP_ADAPTER(pIfPort); + INTNETTRUNKIFSTATE enmOldTrunkState; + + LogFlow(("vboxNetAdpWinPortSetState: pThis=%p, pIfPort=%p, enmState=%d\n", pThis, pIfPort, enmState)); + /* + * Input validation. + */ + AssertPtr(pThis); + AssertPtr(pThis->pGlobals); + Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION); + AssertPtrReturn(pThis->pSwitchPort, INTNETTRUNKIFSTATE_INVALID); + AssertReturn(enmState > INTNETTRUNKIFSTATE_INVALID && enmState < INTNETTRUNKIFSTATE_END, + INTNETTRUNKIFSTATE_INVALID); + + enmOldTrunkState = pThis->enmTrunkState; + if (enmOldTrunkState != enmState) + ASMAtomicWriteU32((uint32_t volatile *)&pThis->enmTrunkState, enmState); + + return enmOldTrunkState; +} + +/** + * @copydoc INTNETTRUNKIFPORT::pfnWaitForIdle + */ +static DECLCALLBACK(int) vboxNetAdpWinPortWaitForIdle(PINTNETTRUNKIFPORT pIfPort, uint32_t cMillies) +{ + PVBOXNETADP_ADAPTER pThis = IFPORT_2_VBOXNETADP_ADAPTER(pIfPort); + int rc; + + LogFlow(("vboxNetAdpWinPortWaitForIdle: pThis=%p, pIfPort=%p, cMillies=%u\n", pThis, pIfPort, cMillies)); + /* + * Input validation. + */ + AssertPtr(pThis); + Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION); + AssertPtrReturn(pThis->pSwitchPort, VERR_INVALID_STATE); + AssertReturn(pThis->enmTrunkState == INTNETTRUNKIFSTATE_DISCONNECTING, VERR_INVALID_STATE); + + rc = NdisWaitEvent(&pThis->EventIdle, cMillies) ? VINF_SUCCESS : VERR_TIMEOUT; + + return rc; +} + +/** + * @copydoc INTNETTRUNKIFPORT::pfnXmit + */ +static DECLCALLBACK(int) vboxNetAdpWinPortXmit(PINTNETTRUNKIFPORT pIfPort, void *pvIfData, PINTNETSG pSG, uint32_t fDst) +{ + RT_NOREF1(fDst); + PVBOXNETADP_ADAPTER pThis = IFPORT_2_VBOXNETADP_ADAPTER(pIfPort); + int rc = VINF_SUCCESS; + + LogFlow(("vboxNetAdpWinPortXmit: pThis=%p, pIfPort=%p, pvIfData=%p, pSG=%p, fDst=0x%x\n", pThis, pIfPort, pvIfData, pSG, fDst)); + RT_NOREF1(pvIfData); + /* + * Input validation. + */ + AssertPtr(pThis); + AssertPtr(pSG); + Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION); + AssertPtrReturn(pThis->pSwitchPort, VERR_INVALID_STATE); + + vboxNetAdpWinDumpPacket(pSG, "intnet --> host"); + + /* + * First of all, indicate we are busy. It is possible the trunk or the adapter + * will get paused or even disconnected, so we need to check the state after + * we have marked ourselves busy. + * Later, when NDIS returns all buffers, we will mark ourselves idle. + */ + if (ASMAtomicIncS32(&pThis->cBusy) == 1) + NdisResetEvent(&pThis->EventIdle); + + if (vboxNetAdpWinIsActive(pThis)) + { + PNET_BUFFER_LIST pBufList = vboxNetAdpWinSGtoNB(pThis, pSG); + if (pBufList) + { + NdisMIndicateReceiveNetBufferLists(pThis->hAdapter, pBufList, NDIS_DEFAULT_PORT_NUMBER, 1, 0); + vboxNetAdpWinUpdateStats(pThis->au64StatsInPackets, pThis->au64StatsInOctets, pSG); + } + } + + return rc; +} + +/** + * @copydoc INTNETTRUNKIFPORT::pfnNotifyMacAddress + */ +static DECLCALLBACK(void) vboxNetAdpWinPortNotifyMacAddress(PINTNETTRUNKIFPORT pIfPort, void *pvIfData, PCRTMAC pMac) +{ + PVBOXNETADP_ADAPTER pThis = IFPORT_2_VBOXNETADP_ADAPTER(pIfPort); + + LogFlow(("vboxNetAdpWinPortNotifyMacAddress: pThis=%p, pIfPort=%p, pvIfData=%p, pMac=%p\n", pThis, pIfPort, pvIfData, pMac)); + RT_NOREF3(pThis, pvIfData, pMac); + /* + * Input validation. + */ + AssertPtr(pThis); + Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION); + + /// @todo Do we really need to handle this? +} + + +/** + * @copydoc INTNETTRUNKIFPORT::pfnConnectInterface + */ +static DECLCALLBACK(int) vboxNetAdpWinPortConnectInterface(PINTNETTRUNKIFPORT pIfPort, void *pvIf, void **ppvIfData) +{ + PVBOXNETADP_ADAPTER pThis = IFPORT_2_VBOXNETADP_ADAPTER(pIfPort); + int rc; + + LogFlow(("vboxNetAdpWinPortConnectInterface: pThis=%p, pIfPort=%p, pvIf=%p, ppvIfData=%p\n", pThis, pIfPort, pvIf, ppvIfData)); + RT_NOREF3(pThis, pvIf, ppvIfData); + /* + * Input validation. + */ + AssertPtr(pThis); + Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION); + + rc = VINF_SUCCESS; + + return rc; +} + + +/** + * @copydoc INTNETTRUNKIFPORT::pfnDisconnectInterface + */ +static DECLCALLBACK(void) vboxNetAdpWinPortDisconnectInterface(PINTNETTRUNKIFPORT pIfPort, void *pvIfData) +{ + PVBOXNETADP_ADAPTER pThis = IFPORT_2_VBOXNETADP_ADAPTER(pIfPort); + int rc; + + LogFlow(("vboxNetAdpWinPortDisconnectInterface: pThis=%p, pIfPort=%p, pvIfData=%p\n", pThis, pIfPort, pvIfData)); + RT_NOREF2(pThis, pvIfData); + /* + * Input validation. + */ + AssertPtr(pThis); + Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION); + + rc = VINF_SUCCESS; + AssertRC(rc); +} + + + +/** + * Implements the SUPDRV component factor interface query method. + * + * @returns Pointer to an interface. NULL if not supported. + * + * @param pSupDrvFactory Pointer to the component factory registration structure. + * @param pSession The session - unused. + * @param pszInterfaceUuid The factory interface id. + */ +static DECLCALLBACK(void *) vboxNetAdpWinQueryFactoryInterface(PCSUPDRVFACTORY pSupDrvFactory, PSUPDRVSESSION pSession, + const char *pszInterfaceUuid) +{ + PVBOXNETADPGLOBALS pGlobals = (PVBOXNETADPGLOBALS)((uint8_t *)pSupDrvFactory - RT_UOFFSETOF(VBOXNETADPGLOBALS, SupDrvFactory)); + + /* + * Convert the UUID strings and compare them. + */ + RTUUID UuidReq; + int rc = RTUuidFromStr(&UuidReq, pszInterfaceUuid); + if (RT_SUCCESS(rc)) + { + if (!RTUuidCompareStr(&UuidReq, INTNETTRUNKFACTORY_UUID_STR)) + { + NdisAcquireSpinLock(&pGlobals->Lock); + if (pGlobals->enmIdcState == kVBoxNetAdpWinIdcState_Connected) + { + pGlobals->cFactoryRefs++; + NdisResetEvent(&pGlobals->EventUnloadAllowed); + } + NdisReleaseSpinLock(&pGlobals->Lock); + return &pGlobals->TrunkFactory; + } +#ifdef LOG_ENABLED + else + Log(("VBoxNetFlt: unknown factory interface query (%s)\n", pszInterfaceUuid)); +#endif + } + else + Log(("VBoxNetFlt: rc=%Rrc, uuid=%s\n", rc, pszInterfaceUuid)); + + RT_NOREF1(pSession); + return NULL; +} + + +DECLHIDDEN(void) vboxNetAdpWinReportCapabilities(PVBOXNETADP_ADAPTER pThis) +{ + if (pThis->pSwitchPort) + { + pThis->pSwitchPort->pfnReportMacAddress(pThis->pSwitchPort, &pThis->MacAddr); + /* Promiscuous mode makes no sense for host-only adapters, does it? */ + pThis->pSwitchPort->pfnReportGsoCapabilities(pThis->pSwitchPort, 0, + INTNETTRUNKDIR_WIRE | INTNETTRUNKDIR_HOST); + pThis->pSwitchPort->pfnReportNoPreemptDsts(pThis->pSwitchPort, 0 /* none */); + } +} + +/** + * @copydoc INTNETTRUNKFACTORY::pfnCreateAndConnect + */ +static DECLCALLBACK(int) vboxNetAdpWinFactoryCreateAndConnect(PINTNETTRUNKFACTORY pIfFactory, const char *pszName, + PINTNETTRUNKSWPORT pSwitchPort, uint32_t fFlags, + PINTNETTRUNKIFPORT *ppIfPort) +{ + PVBOXNETADPGLOBALS pGlobals = (PVBOXNETADPGLOBALS)((uint8_t *)pIfFactory - RT_UOFFSETOF(VBOXNETADPGLOBALS, TrunkFactory)); + + LogFlow(("==>vboxNetAdpWinFactoryCreateAndConnect: pszName=%p:{%s} fFlags=%#x\n", pszName, pszName, fFlags)); + Assert(pGlobals->cFactoryRefs > 0); + AssertMsgReturn(!(fFlags & ~(INTNETTRUNKFACTORY_FLAG_NO_PROMISC)), + ("%#x\n", fFlags), VERR_INVALID_PARAMETER); + + DbgPrint("vboxNetAdpWinFactoryCreateAndConnect: looking for %s...\n", pszName); + PVBOXNETADP_ADAPTER pAdapter = NULL; + NdisAcquireSpinLock(&pGlobals->Lock); + RTListForEach(&g_VBoxNetAdpGlobals.ListOfAdapters, pAdapter, VBOXNETADP_ADAPTER, node) + { + Log(("vboxNetAdpWinFactoryCreateAndConnect: evaluating adapter=%s\n", pAdapter->szName)); + DbgPrint("vboxNetAdpWinFactoryCreateAndConnect: evaluating %s...\n", pAdapter->szName); + if (!RTStrICmp(pszName, pAdapter->szName)) + { + pAdapter->pSwitchPort = pSwitchPort; + *ppIfPort = &pAdapter->MyPort; + NdisReleaseSpinLock(&g_VBoxNetAdpGlobals.Lock); /// @todo too early? adp should have been connected by the time we do this + Log(("vboxNetAdpWinFactoryCreateAndConnect: found matching adapter, name=%s\n", pszName)); + vboxNetAdpWinReportCapabilities(pAdapter); + /// @todo I guess there is no need in vboxNetAdpWinRegisterIpAddrNotifier(pThis); + LogFlow(("<==vboxNetAdpWinFactoryCreateAndConnect: return VINF_SUCCESS\n")); + return VINF_SUCCESS; + } + } + NdisReleaseSpinLock(&pGlobals->Lock); + /// @todo vboxNetAdpLogErrorEvent(IO_ERR_INTERNAL_ERROR, STATUS_SUCCESS, 6); + DbgPrint("vboxNetAdpWinFactoryCreateAndConnect: could not find %s\n", pszName); + LogFlow(("<==vboxNetAdpWinFactoryCreateAndConnect: return VERR_INTNET_FLT_IF_NOT_FOUND\n")); + return VERR_INTNET_FLT_IF_NOT_FOUND; +} + + +/** + * @copydoc INTNETTRUNKFACTORY::pfnRelease + */ +static DECLCALLBACK(void) vboxNetAdpWinFactoryRelease(PINTNETTRUNKFACTORY pIfFactory) +{ + PVBOXNETADPGLOBALS pGlobals = (PVBOXNETADPGLOBALS)((uint8_t *)pIfFactory - RT_OFFSETOF(VBOXNETADPGLOBALS, TrunkFactory)); + + NdisAcquireSpinLock(&pGlobals->Lock); + int32_t cRefs = ASMAtomicDecS32(&pGlobals->cFactoryRefs); + if (cRefs == 0) + NdisSetEvent(&pGlobals->EventUnloadAllowed); + NdisReleaseSpinLock(&pGlobals->Lock); + Assert(cRefs >= 0); NOREF(cRefs); + LogFlow(("vboxNetAdpWinFactoryRelease: cRefs=%d (new)\n", cRefs)); +} + + + +/* IDC */ + +DECLINLINE(const char *) vboxNetAdpWinIdcStateToText(uint32_t enmState) +{ + switch (enmState) + { + case kVBoxNetAdpWinIdcState_Disconnected: return "Disconnected"; + case kVBoxNetAdpWinIdcState_Connecting: return "Connecting"; + case kVBoxNetAdpWinIdcState_Connected: return "Connected"; + case kVBoxNetAdpWinIdcState_Stopping: return "Stopping"; + } + return "Unknown"; +} + +static VOID vboxNetAdpWinInitIdcWorker(PVOID pvContext) +{ + int rc; + PVBOXNETADPGLOBALS pGlobals = (PVBOXNETADPGLOBALS)pvContext; + + /* + * Note that we break the rules here and access IDC state wihout acquiring + * the lock. This is ok because vboxNetAdpWinUnload will wait for this + * thread to terminate itself and we always use atomic access to IDC state. + * We check the state (while holding the lock) further when we have succeeded + * to connect. We cannot take the lock here and release it later as we will + * be holding it for too long. + */ + while (ASMAtomicReadU32(&pGlobals->enmIdcState) == kVBoxNetAdpWinIdcState_Connecting) + { + /* + * Establish a connection to SUPDRV and register our component factory. + */ + rc = SUPR0IdcOpen(&pGlobals->SupDrvIDC, 0 /* iReqVersion = default */, 0 /* iMinVersion = default */, NULL, NULL, NULL); + if (RT_SUCCESS(rc)) + { + rc = SUPR0IdcComponentRegisterFactory(&pGlobals->SupDrvIDC, &pGlobals->SupDrvFactory); + if (RT_SUCCESS(rc)) + { + /* + * At this point we should take the lock to access IDC state as + * we technically may now race with factory methods. + */ + NdisAcquireSpinLock(&pGlobals->Lock); + bool fSuccess = ASMAtomicCmpXchgU32(&pGlobals->enmIdcState, + kVBoxNetAdpWinIdcState_Connected, + kVBoxNetAdpWinIdcState_Connecting); + NdisReleaseSpinLock(&pGlobals->Lock); + if (!fSuccess) + { + /* The state has been changed (the only valid transition is to "Stopping"), undo init */ + rc = SUPR0IdcComponentDeregisterFactory(&pGlobals->SupDrvIDC, &pGlobals->SupDrvFactory); + AssertRC(rc); + SUPR0IdcClose(&pGlobals->SupDrvIDC); + Log(("vboxNetAdpWinInitIdcWorker: state change (Connecting -> %s) while initializing IDC, closed IDC, rc=0x%x\n", + vboxNetAdpWinIdcStateToText(ASMAtomicReadU32(&pGlobals->enmIdcState)), rc)); + } + else + { + Log(("vboxNetAdpWinInitIdcWorker: IDC state change Connecting -> Connected\n")); + } + } + } + else + { + LARGE_INTEGER WaitIn100nsUnits; + WaitIn100nsUnits.QuadPart = -(LONGLONG)5000000; /* 0.5 sec */ + KeDelayExecutionThread(KernelMode, FALSE /* non-alertable */, &WaitIn100nsUnits); + } + } + PsTerminateSystemThread(STATUS_SUCCESS); +} + + +DECLHIDDEN(int) vboxNetAdpWinStartInitIdcThread(PVBOXNETADPGLOBALS pGlobals) +{ + int rc = VERR_INVALID_STATE; + + /* No locking needed yet */ + if (ASMAtomicCmpXchgU32(&pGlobals->enmIdcState, kVBoxNetAdpWinIdcState_Connecting, kVBoxNetAdpWinIdcState_Disconnected)) + { + Log(("vboxNetAdpWinStartInitIdcThread: IDC state change Diconnected -> Connecting\n")); + + NTSTATUS Status = PsCreateSystemThread(&g_VBoxNetAdpGlobals.hInitIdcThread, + THREAD_ALL_ACCESS, + NULL, + NULL, + NULL, + vboxNetAdpWinInitIdcWorker, + &g_VBoxNetAdpGlobals); + Log(("vboxNetAdpWinStartInitIdcThread: create IDC initialization thread, status=0x%x\n", Status)); + if (Status != STATUS_SUCCESS) + { + LogError(("vboxNetAdpWinStartInitIdcThread: IDC initialization failed (system thread creation, status=0x%x)\n", Status)); + /* + * We failed to init IDC and there will be no second chance. + */ + Log(("vboxNetAdpWinStartInitIdcThread: IDC state change Connecting -> Diconnected\n")); + ASMAtomicWriteU32(&g_VBoxNetAdpGlobals.enmIdcState, kVBoxNetAdpWinIdcState_Disconnected); + } + rc = RTErrConvertFromNtStatus(Status); + } + return rc; +} + + + +/* === !!!! */ + + +NDIS_OID g_SupportedOids[] = +{ + OID_GEN_CURRENT_LOOKAHEAD, + OID_GEN_CURRENT_PACKET_FILTER, + OID_GEN_INTERRUPT_MODERATION, + OID_GEN_LINK_PARAMETERS, + OID_GEN_MAXIMUM_TOTAL_SIZE, + OID_GEN_RCV_OK, + OID_GEN_RECEIVE_BLOCK_SIZE, + OID_GEN_RECEIVE_BUFFER_SPACE, + OID_GEN_STATISTICS, + OID_GEN_TRANSMIT_BLOCK_SIZE, + OID_GEN_TRANSMIT_BUFFER_SPACE, + OID_GEN_VENDOR_DESCRIPTION, + OID_GEN_VENDOR_DRIVER_VERSION, + OID_GEN_VENDOR_ID, + OID_GEN_XMIT_OK, + OID_802_3_PERMANENT_ADDRESS, + OID_802_3_CURRENT_ADDRESS, + OID_802_3_MULTICAST_LIST, + OID_802_3_MAXIMUM_LIST_SIZE, + OID_PNP_CAPABILITIES, + OID_PNP_QUERY_POWER, + OID_PNP_SET_POWER +}; + +DECLHIDDEN(NDIS_STATUS) vboxNetAdpWinAllocAdapter(NDIS_HANDLE hAdapter, PVBOXNETADP_ADAPTER *ppAdapter, ULONG uIfIndex) +{ + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + PVBOXNETADP_ADAPTER pAdapter = NULL; + PVBOXNETADPGLOBALS pGlobals = &g_VBoxNetAdpGlobals; + + LogFlow(("==>vboxNetAdpWinAllocAdapter: adapter handle=%p\n", hAdapter)); + + /* Get the name */ + UNICODE_STRING strUnicodeName; + Status = NdisMQueryAdapterInstanceName(&strUnicodeName, hAdapter); + if (Status != NDIS_STATUS_SUCCESS) + { + LogError(("vboxNetAdpWinAllocAdapter: NdisMQueryAdapterInstanceName failed with 0x%x\n", Status)); + return Status; + } + + ANSI_STRING strAnsiName; + /* We use the miniport name to associate this filter module with the netflt instance */ + NTSTATUS rc = RtlUnicodeStringToAnsiString(&strAnsiName, + &strUnicodeName, + TRUE); + if (rc != STATUS_SUCCESS) + { + LogError(("vboxNetAdpWinAllocAdapter: RtlUnicodeStringToAnsiString(%ls) failed with 0x%x\n", + strUnicodeName, rc)); + //vboxNetAdpLogErrorEvent(IO_ERR_INTERNAL_ERROR, NDIS_STATUS_FAILURE, 2); + NdisFreeMemory(strUnicodeName.Buffer, 0, 0); + return NDIS_STATUS_FAILURE; + } + NdisFreeMemory(strUnicodeName.Buffer, 0, 0); + DbgPrint("vboxNetAdpWinAllocAdapter: name=%Z\n", &strAnsiName); + + *ppAdapter = NULL; + + UINT cbAdapterWithNameExtra = sizeof(VBOXNETADP_ADAPTER) + strAnsiName.Length; + pAdapter = (PVBOXNETADP_ADAPTER)NdisAllocateMemoryWithTagPriority(pGlobals->hMiniportDriver, + cbAdapterWithNameExtra, + VBOXNETADPWIN_TAG, + NormalPoolPriority); + if (!pAdapter) + { + RtlFreeAnsiString(&strAnsiName); + Status = NDIS_STATUS_RESOURCES; + Log(("vboxNetAdpWinAllocAdapter: Out of memory while allocating adapter context (size=%d)\n", sizeof(VBOXNETADP_ADAPTER))); + } + else + { + NdisZeroMemory(pAdapter, cbAdapterWithNameExtra); + NdisMoveMemory(pAdapter->szName, strAnsiName.Buffer, strAnsiName.Length); + RtlFreeAnsiString(&strAnsiName); + + /* Allocate buffer pool */ + NET_BUFFER_LIST_POOL_PARAMETERS PoolParams; + NdisZeroMemory(&PoolParams, sizeof(PoolParams)); + PoolParams.Header.Type = NDIS_OBJECT_TYPE_DEFAULT; + PoolParams.Header.Revision = NET_BUFFER_LIST_POOL_PARAMETERS_REVISION_1; + PoolParams.Header.Size = sizeof(PoolParams); + PoolParams.ProtocolId = NDIS_PROTOCOL_ID_DEFAULT; + PoolParams.fAllocateNetBuffer = TRUE; + PoolParams.ContextSize = 0; + PoolParams.PoolTag = VBOXNETADP_MEM_TAG; + pAdapter->hPool = NdisAllocateNetBufferListPool(hAdapter, &PoolParams); + if (!pAdapter->hPool) + { + LogError(("vboxNetAdpWinAllocAdapter: NdisAllocateNetBufferListPool failed\n")); + NdisFreeMemory(pAdapter, 0, 0); + return NDIS_STATUS_RESOURCES; + } + Log4(("vboxNetAdpWinAllocAdapter: allocated NBL+NB pool 0x%p\n", pAdapter->hPool)); + + pAdapter->hAdapter = hAdapter; + pAdapter->MyPort.u32Version = INTNETTRUNKIFPORT_VERSION; + pAdapter->MyPort.pfnRetain = vboxNetAdpWinPortRetain; + pAdapter->MyPort.pfnRelease = vboxNetAdpWinPortRelease; + pAdapter->MyPort.pfnDisconnectAndRelease = vboxNetAdpWinPortDisconnectAndRelease; + pAdapter->MyPort.pfnSetState = vboxNetAdpWinPortSetState; + pAdapter->MyPort.pfnWaitForIdle = vboxNetAdpWinPortWaitForIdle; + pAdapter->MyPort.pfnXmit = vboxNetAdpWinPortXmit; + pAdapter->MyPort.pfnNotifyMacAddress = vboxNetAdpWinPortNotifyMacAddress; + pAdapter->MyPort.pfnConnectInterface = vboxNetAdpWinPortConnectInterface; + pAdapter->MyPort.pfnDisconnectInterface = vboxNetAdpWinPortDisconnectInterface; + pAdapter->MyPort.u32VersionEnd = INTNETTRUNKIFPORT_VERSION; + pAdapter->pGlobals = pGlobals; + pAdapter->enmAdapterState = kVBoxNetAdpWinState_Initializing; + pAdapter->enmTrunkState = INTNETTRUNKIFSTATE_INACTIVE; + pAdapter->cBusy = 0; + NdisInitializeEvent(&pAdapter->EventIdle); + NdisSetEvent(&pAdapter->EventIdle); /* We are idle initially */ + + /* Use a locally administered version of the OUI we use for the guest NICs. */ + pAdapter->MacAddr.au8[0] = 0x08 | 2; + pAdapter->MacAddr.au8[1] = 0x00; + pAdapter->MacAddr.au8[2] = 0x27; + + pAdapter->MacAddr.au8[3] = (uIfIndex >> 16) & 0xFF; + pAdapter->MacAddr.au8[4] = (uIfIndex >> 8) & 0xFF; + pAdapter->MacAddr.au8[5] = uIfIndex & 0xFF; + + NdisAcquireSpinLock(&pGlobals->Lock); + RTListPrepend(&pGlobals->ListOfAdapters, &pAdapter->node); + NdisReleaseSpinLock(&pGlobals->Lock); + + *ppAdapter = pAdapter; + } + LogFlow(("<==vboxNetAdpWinAllocAdapter: status=0x%x\n", Status)); + return Status; +} + +DECLHIDDEN(void) vboxNetAdpWinFreeAdapter(PVBOXNETADP_ADAPTER pAdapter) +{ + /* Remove from adapter chain */ + NdisAcquireSpinLock(&pAdapter->pGlobals->Lock); + RTListNodeRemove(&pAdapter->node); + NdisReleaseSpinLock(&pAdapter->pGlobals->Lock); + + NdisFreeMemory(pAdapter, 0, 0); +} + +DECLINLINE(NDIS_MEDIA_CONNECT_STATE) vboxNetAdpWinGetConnectState(PVBOXNETADP_ADAPTER pAdapter) +{ + RT_NOREF1(pAdapter); + return MediaConnectStateConnected; +} + + +DECLHIDDEN(NDIS_STATUS) vboxNetAdpWinInitializeEx(IN NDIS_HANDLE NdisMiniportHandle, + IN NDIS_HANDLE MiniportDriverContext, + IN PNDIS_MINIPORT_INIT_PARAMETERS MiniportInitParameters) +{ + RT_NOREF1(MiniportDriverContext); + PVBOXNETADP_ADAPTER pAdapter = NULL; + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + + LogFlow(("==>vboxNetAdpWinInitializeEx: miniport=0x%x\n", NdisMiniportHandle)); + + do + { + NDIS_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES RAttrs = {{0}}; + NDIS_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES GAttrs = {{0}}; + + Status = vboxNetAdpWinAllocAdapter(NdisMiniportHandle, &pAdapter, MiniportInitParameters->IfIndex); + if (Status != NDIS_STATUS_SUCCESS) + { + Log(("vboxNetAdpWinInitializeEx: Failed to allocate the adapter context with 0x%x\n", Status)); + break; + } + + RAttrs.Header.Type = NDIS_OBJECT_TYPE_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES; + RAttrs.Header.Size = NDIS_SIZEOF_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES_REVISION_1; + RAttrs.Header.Revision = NDIS_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES_REVISION_1; + RAttrs.MiniportAdapterContext = pAdapter; + RAttrs.AttributeFlags = VBOXNETADPWIN_ATTR_FLAGS; // NDIS_MINIPORT_ATTRIBUTES_NDIS_WDM + RAttrs.CheckForHangTimeInSeconds = VBOXNETADPWIN_HANG_CHECK_TIME; + RAttrs.InterfaceType = NdisInterfaceInternal; + + Status = NdisMSetMiniportAttributes(NdisMiniportHandle, + (PNDIS_MINIPORT_ADAPTER_ATTRIBUTES)&RAttrs); + if (Status != NDIS_STATUS_SUCCESS) + { + Log(("vboxNetAdpWinInitializeEx: NdisMSetMiniportAttributes(registration) failed with 0x%x\n", Status)); + break; + } + + /// @todo Registry? + + /// @todo WDM stack? + + /// @todo DPC? + + GAttrs.Header.Type = NDIS_OBJECT_TYPE_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES; + GAttrs.Header.Size = NDIS_SIZEOF_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES_REVISION_1; + GAttrs.Header.Revision = NDIS_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES_REVISION_1; + + GAttrs.MediaType = NdisMedium802_3; + GAttrs.PhysicalMediumType = NdisPhysicalMediumUnspecified; + GAttrs.MtuSize = 1500; /// @todo + GAttrs.MaxXmitLinkSpeed = VBOXNETADPWIN_LINK_SPEED; + GAttrs.XmitLinkSpeed = VBOXNETADPWIN_LINK_SPEED; + GAttrs.MaxRcvLinkSpeed = VBOXNETADPWIN_LINK_SPEED; + GAttrs.RcvLinkSpeed = VBOXNETADPWIN_LINK_SPEED; + GAttrs.MediaConnectState = vboxNetAdpWinGetConnectState(pAdapter); + GAttrs.MediaDuplexState = MediaDuplexStateFull; + GAttrs.LookaheadSize = 1500; /// @todo + GAttrs.MacOptions = VBOXNETADP_MAC_OPTIONS; + GAttrs.SupportedPacketFilters = VBOXNETADP_SUPPORTED_FILTERS; + GAttrs.MaxMulticastListSize = 32; /// @todo + + GAttrs.MacAddressLength = ETH_LENGTH_OF_ADDRESS; + Assert(GAttrs.MacAddressLength == sizeof(pAdapter->MacAddr)); + memcpy(GAttrs.PermanentMacAddress, pAdapter->MacAddr.au8, GAttrs.MacAddressLength); + memcpy(GAttrs.CurrentMacAddress, pAdapter->MacAddr.au8, GAttrs.MacAddressLength); + + GAttrs.RecvScaleCapabilities = NULL; + GAttrs.AccessType = NET_IF_ACCESS_BROADCAST; + GAttrs.DirectionType = NET_IF_DIRECTION_SENDRECEIVE; + GAttrs.ConnectionType = NET_IF_CONNECTION_DEDICATED; + GAttrs.IfType = IF_TYPE_ETHERNET_CSMACD; + GAttrs.IfConnectorPresent = false; + GAttrs.SupportedStatistics = VBOXNETADPWIN_SUPPORTED_STATISTICS; + GAttrs.SupportedPauseFunctions = NdisPauseFunctionsUnsupported; + GAttrs.DataBackFillSize = 0; + GAttrs.ContextBackFillSize = 0; + GAttrs.SupportedOidList = g_SupportedOids; + GAttrs.SupportedOidListLength = sizeof(g_SupportedOids); + GAttrs.AutoNegotiationFlags = NDIS_LINK_STATE_DUPLEX_AUTO_NEGOTIATED; + GAttrs.PowerManagementCapabilities = &g_VBoxNetAdpGlobals.PMCaps; + + Status = NdisMSetMiniportAttributes(NdisMiniportHandle, + (PNDIS_MINIPORT_ADAPTER_ATTRIBUTES)&GAttrs); + if (Status != NDIS_STATUS_SUCCESS) + { + Log(("vboxNetAdpWinInitializeEx: NdisMSetMiniportAttributes(general) failed with 0x%x\n", Status)); + break; + } + + VBOXNETADPWIN_ADAPTER_STATE enmPrevState = vboxNetAdpWinSetState(pAdapter, kVBoxNetAdpWinState_Paused); + RT_NOREF1(enmPrevState); + Assert(enmPrevState == kVBoxNetAdpWinState_Initializing); + } while (false); + + if (Status != NDIS_STATUS_SUCCESS) + { + if (pAdapter) + vboxNetAdpWinFreeAdapter(pAdapter); + } + + LogFlow(("<==vboxNetAdpWinInitializeEx: status=0x%x\n", Status)); + return Status; +} + +DECLHIDDEN(VOID) vboxNetAdpWinHaltEx(IN NDIS_HANDLE MiniportAdapterContext, + IN NDIS_HALT_ACTION HaltAction) +{ + RT_NOREF1(HaltAction); + PVBOXNETADP_ADAPTER pThis = (PVBOXNETADP_ADAPTER)MiniportAdapterContext; + LogFlow(("==>vboxNetAdpWinHaltEx\n")); + AssertPtr(pThis); + Assert(vboxNetAdpWinGetState(pThis) == kVBoxNetAdpWinState_Paused); + /* + * Check if the trunk is active which means the adapter gets disabled + * while it is used by VM(s) and we need to disconnect the trunk. + */ + if (pThis->pSwitchPort && pThis->enmTrunkState == INTNETTRUNKIFSTATE_ACTIVE) + pThis->pSwitchPort->pfnDisconnect(pThis->pSwitchPort, &pThis->MyPort, NULL); + /* + * Since we are already in the paused state and we have disconnected + * the trunk, we can safely destroy this adapter. + */ + vboxNetAdpWinFreeAdapter(pThis); + LogFlow(("<==vboxNetAdpWinHaltEx\n")); +} + +DECLHIDDEN(NDIS_STATUS) vboxNetAdpWinPause(IN NDIS_HANDLE MiniportAdapterContext, + IN PNDIS_MINIPORT_PAUSE_PARAMETERS MiniportPauseParameters) +{ + RT_NOREF1(MiniportPauseParameters); + PVBOXNETADP_ADAPTER pThis = (PVBOXNETADP_ADAPTER)MiniportAdapterContext; + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + LogFlow(("==>vboxNetAdpWinPause\n")); + VBOXNETADPWIN_ADAPTER_STATE enmPrevState = vboxNetAdpWinSetState(pThis, kVBoxNetAdpWinState_Pausing); + Assert(enmPrevState == kVBoxNetAdpWinState_Running); + if (!NdisWaitEvent(&pThis->EventIdle, 1000 /* ms */)) + { + LogError(("vboxNetAdpWinPause: timed out while pausing the adapter\n")); + /// @todo implement NDIS_STATUS_PENDING case? probably not. + } + enmPrevState = vboxNetAdpWinSetState(pThis, kVBoxNetAdpWinState_Paused); + Assert(enmPrevState == kVBoxNetAdpWinState_Pausing); + LogFlow(("<==vboxNetAdpWinPause: status=0x%x\n", Status)); + return Status; +} + +DECLHIDDEN(NDIS_STATUS) vboxNetAdpWinRestart(IN NDIS_HANDLE MiniportAdapterContext, + IN PNDIS_MINIPORT_RESTART_PARAMETERS MiniportRestartParameters) +{ + RT_NOREF1(MiniportRestartParameters); + PVBOXNETADP_ADAPTER pThis = (PVBOXNETADP_ADAPTER)MiniportAdapterContext; + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + LogFlow(("==>vboxNetAdpWinRestart\n")); + VBOXNETADPWIN_ADAPTER_STATE enmPrevState = vboxNetAdpWinSetState(pThis, kVBoxNetAdpWinState_Restarting); + Assert(enmPrevState == kVBoxNetAdpWinState_Paused); + /// @todo anything? + enmPrevState = vboxNetAdpWinSetState(pThis, kVBoxNetAdpWinState_Running); + Assert(enmPrevState == kVBoxNetAdpWinState_Restarting); + LogFlow(("<==vboxNetAdpWinRestart: status=0x%x\n", Status)); + return Status; +} + +DECLINLINE(uint64_t) vboxNetAdpWinStatsTotals(uint64_t *pStats) +{ + return pStats[kVBoxNetAdpWinPacketType_Unicast] + + pStats[kVBoxNetAdpWinPacketType_Multicast] + + pStats[kVBoxNetAdpWinPacketType_Broadcast]; +} + +DECLINLINE(PVOID) vboxNetAdpWinStatsU64(uint64_t *pTmp, ULONG *pcbTmp, uint64_t u64Stat) +{ + *pcbTmp = sizeof(*pTmp); + *pTmp = u64Stat; + return pTmp; +} + +DECLHIDDEN(NDIS_STATUS) vboxNetAdpWinOidRqQuery(PVBOXNETADP_ADAPTER pThis, + PNDIS_OID_REQUEST pRequest) +{ + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + struct _NDIS_OID_REQUEST::_REQUEST_DATA::_QUERY *pQuery = &pRequest->DATA.QUERY_INFORMATION; + + LogFlow(("==>vboxNetAdpWinOidRqQuery\n")); + + uint64_t u64Tmp = 0; + ULONG ulTmp = 0; + PVOID pInfo = &ulTmp; + ULONG cbInfo = sizeof(ulTmp); + + switch (pQuery->Oid) + { + case OID_GEN_INTERRUPT_MODERATION: + { + PNDIS_INTERRUPT_MODERATION_PARAMETERS pParams = + (PNDIS_INTERRUPT_MODERATION_PARAMETERS)pQuery->InformationBuffer; + cbInfo = NDIS_SIZEOF_INTERRUPT_MODERATION_PARAMETERS_REVISION_1; + if (cbInfo > pQuery->InformationBufferLength) + break; + pParams->Header.Type = NDIS_OBJECT_TYPE_DEFAULT; + pParams->Header.Revision = NDIS_INTERRUPT_MODERATION_PARAMETERS_REVISION_1; + pParams->Header.Size = NDIS_SIZEOF_INTERRUPT_MODERATION_PARAMETERS_REVISION_1; + pParams->Flags = 0; + pParams->InterruptModeration = NdisInterruptModerationNotSupported; + pInfo = NULL; /* Do not copy */ + break; + } + case OID_GEN_MAXIMUM_TOTAL_SIZE: + case OID_GEN_RECEIVE_BLOCK_SIZE: + case OID_GEN_TRANSMIT_BLOCK_SIZE: + ulTmp = VBOXNETADP_MAX_FRAME_SIZE; + break; + case OID_GEN_RECEIVE_BUFFER_SPACE: + case OID_GEN_TRANSMIT_BUFFER_SPACE: + /// @todo Make configurable + ulTmp = VBOXNETADP_MAX_FRAME_SIZE * 40; + break; + case OID_GEN_RCV_OK: + pInfo = vboxNetAdpWinStatsU64(&u64Tmp, &cbInfo, vboxNetAdpWinStatsTotals(pThis->au64StatsInPackets)); + break; + case OID_GEN_XMIT_OK: + pInfo = vboxNetAdpWinStatsU64(&u64Tmp, &cbInfo, vboxNetAdpWinStatsTotals(pThis->au64StatsOutPackets)); + break; + case OID_GEN_STATISTICS: + { + PNDIS_STATISTICS_INFO pStats = + (PNDIS_STATISTICS_INFO)pQuery->InformationBuffer; + cbInfo = NDIS_SIZEOF_STATISTICS_INFO_REVISION_1; + if (cbInfo > pQuery->InformationBufferLength) + break; + pInfo = NULL; /* Do not copy */ + memset(pStats, 0, cbInfo); + pStats->Header.Type = NDIS_OBJECT_TYPE_DEFAULT; + pStats->Header.Revision = NDIS_STATISTICS_INFO_REVISION_1; + pStats->Header.Size = NDIS_SIZEOF_STATISTICS_INFO_REVISION_1; + pStats->SupportedStatistics = + NDIS_STATISTICS_FLAGS_VALID_DIRECTED_FRAMES_RCV + | NDIS_STATISTICS_FLAGS_VALID_MULTICAST_FRAMES_RCV + | NDIS_STATISTICS_FLAGS_VALID_BROADCAST_FRAMES_RCV + | NDIS_STATISTICS_FLAGS_VALID_BYTES_RCV + | NDIS_STATISTICS_FLAGS_VALID_RCV_DISCARDS + | NDIS_STATISTICS_FLAGS_VALID_RCV_ERROR + | NDIS_STATISTICS_FLAGS_VALID_DIRECTED_FRAMES_XMIT + | NDIS_STATISTICS_FLAGS_VALID_MULTICAST_FRAMES_XMIT + | NDIS_STATISTICS_FLAGS_VALID_BROADCAST_FRAMES_XMIT + | NDIS_STATISTICS_FLAGS_VALID_BYTES_XMIT + | NDIS_STATISTICS_FLAGS_VALID_XMIT_ERROR + | NDIS_STATISTICS_FLAGS_VALID_XMIT_DISCARDS + | NDIS_STATISTICS_FLAGS_VALID_DIRECTED_BYTES_RCV + | NDIS_STATISTICS_FLAGS_VALID_MULTICAST_BYTES_RCV + | NDIS_STATISTICS_FLAGS_VALID_BROADCAST_BYTES_RCV + | NDIS_STATISTICS_FLAGS_VALID_DIRECTED_BYTES_XMIT + | NDIS_STATISTICS_FLAGS_VALID_MULTICAST_BYTES_XMIT + | NDIS_STATISTICS_FLAGS_VALID_BROADCAST_BYTES_XMIT; + + pStats->ifHCInOctets = vboxNetAdpWinStatsTotals(pThis->au64StatsInOctets); + pStats->ifHCInUcastPkts = ASMAtomicReadU64(&pThis->au64StatsInPackets[kVBoxNetAdpWinPacketType_Unicast]); + pStats->ifHCInMulticastPkts = ASMAtomicReadU64(&pThis->au64StatsInPackets[kVBoxNetAdpWinPacketType_Multicast]); + pStats->ifHCInBroadcastPkts = ASMAtomicReadU64(&pThis->au64StatsInPackets[kVBoxNetAdpWinPacketType_Broadcast]); + pStats->ifHCOutOctets = vboxNetAdpWinStatsTotals(pThis->au64StatsOutOctets);; + pStats->ifHCOutUcastPkts = ASMAtomicReadU64(&pThis->au64StatsOutPackets[kVBoxNetAdpWinPacketType_Unicast]); + pStats->ifHCOutMulticastPkts = ASMAtomicReadU64(&pThis->au64StatsOutPackets[kVBoxNetAdpWinPacketType_Multicast]); + pStats->ifHCOutBroadcastPkts = ASMAtomicReadU64(&pThis->au64StatsOutPackets[kVBoxNetAdpWinPacketType_Broadcast]); + pStats->ifHCInUcastOctets = ASMAtomicReadU64(&pThis->au64StatsInOctets[kVBoxNetAdpWinPacketType_Unicast]); + pStats->ifHCInMulticastOctets = ASMAtomicReadU64(&pThis->au64StatsInOctets[kVBoxNetAdpWinPacketType_Multicast]); + pStats->ifHCInBroadcastOctets = ASMAtomicReadU64(&pThis->au64StatsInOctets[kVBoxNetAdpWinPacketType_Broadcast]); + pStats->ifHCOutUcastOctets = ASMAtomicReadU64(&pThis->au64StatsOutOctets[kVBoxNetAdpWinPacketType_Unicast]); + pStats->ifHCOutMulticastOctets = ASMAtomicReadU64(&pThis->au64StatsOutOctets[kVBoxNetAdpWinPacketType_Multicast]); + pStats->ifHCOutBroadcastOctets = ASMAtomicReadU64(&pThis->au64StatsOutOctets[kVBoxNetAdpWinPacketType_Broadcast]); + break; + } + case OID_GEN_VENDOR_DESCRIPTION: + pInfo = VBOXNETADP_VENDOR_NAME; + cbInfo = sizeof(VBOXNETADP_VENDOR_NAME); + break; + case OID_GEN_VENDOR_DRIVER_VERSION: + ulTmp = (VBOXNETADP_VERSION_NDIS_MAJOR << 16) | VBOXNETADP_VERSION_NDIS_MINOR; + break; + case OID_GEN_VENDOR_ID: + ulTmp = VBOXNETADP_VENDOR_ID; + break; + case OID_802_3_PERMANENT_ADDRESS: + case OID_802_3_CURRENT_ADDRESS: + pInfo = &pThis->MacAddr; + cbInfo = sizeof(pThis->MacAddr); + break; + //case OID_802_3_MULTICAST_LIST: + case OID_802_3_MAXIMUM_LIST_SIZE: + ulTmp = VBOXNETADP_MCAST_LIST_SIZE; + break; + case OID_PNP_CAPABILITIES: + pInfo = &pThis->pGlobals->PMCaps; + cbInfo = sizeof(pThis->pGlobals->PMCaps); + break; + case OID_PNP_QUERY_POWER: + pInfo = NULL; /* Do not copy */ + cbInfo = 0; + break; + default: + Status = NDIS_STATUS_NOT_SUPPORTED; + break; + } + + if (Status == NDIS_STATUS_SUCCESS) + { + if (cbInfo > pQuery->InformationBufferLength) + { + pQuery->BytesNeeded = cbInfo; + Status = NDIS_STATUS_BUFFER_TOO_SHORT; + } + else + { + if (pInfo) + NdisMoveMemory(pQuery->InformationBuffer, pInfo, cbInfo); + pQuery->BytesWritten = cbInfo; + } + } + + LogFlow(("<==vboxNetAdpWinOidRqQuery: status=0x%x\n", Status)); + return Status; +} + +DECLHIDDEN(NDIS_STATUS) vboxNetAdpWinOidRqSet(PVBOXNETADP_ADAPTER pAdapter, + PNDIS_OID_REQUEST pRequest) +{ + RT_NOREF1(pAdapter); + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + struct _NDIS_OID_REQUEST::_REQUEST_DATA::_SET *pSet = &pRequest->DATA.SET_INFORMATION; + + LogFlow(("==>vboxNetAdpWinOidRqSet\n")); + + switch (pSet->Oid) + { + case OID_GEN_CURRENT_LOOKAHEAD: + if (pSet->InformationBufferLength != sizeof(ULONG)) + { + pSet->BytesNeeded = sizeof(ULONG); + Status = NDIS_STATUS_INVALID_LENGTH; + break; + } + /// @todo For the time being we simply ignore lookahead settings. + pSet->BytesRead = sizeof(ULONG); + Status = NDIS_STATUS_SUCCESS; + break; + + case OID_GEN_CURRENT_PACKET_FILTER: + if (pSet->InformationBufferLength != sizeof(ULONG)) + { + pSet->BytesNeeded = sizeof(ULONG); + Status = NDIS_STATUS_INVALID_LENGTH; + break; + } + /// @todo For the time being we simply ignore packet filter settings. + pSet->BytesRead = pSet->InformationBufferLength; + Status = NDIS_STATUS_SUCCESS; + break; + + case OID_GEN_INTERRUPT_MODERATION: + pSet->BytesNeeded = 0; + pSet->BytesRead = 0; + Status = NDIS_STATUS_INVALID_DATA; + break; + + case OID_PNP_SET_POWER: + if (pSet->InformationBufferLength < sizeof(NDIS_DEVICE_POWER_STATE)) + { + Status = NDIS_STATUS_INVALID_LENGTH; + break; + } + pSet->BytesRead = sizeof(NDIS_DEVICE_POWER_STATE); + Status = NDIS_STATUS_SUCCESS; + break; + + default: + Status = NDIS_STATUS_NOT_SUPPORTED; + break; + } + + LogFlow(("<==vboxNetAdpWinOidRqSet: status=0x%x\n", Status)); + return Status; +} + +DECLHIDDEN(NDIS_STATUS) vboxNetAdpWinOidRequest(IN NDIS_HANDLE MiniportAdapterContext, + IN PNDIS_OID_REQUEST NdisRequest) +{ + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + PVBOXNETADP_ADAPTER pAdapter = (PVBOXNETADP_ADAPTER)MiniportAdapterContext; + LogFlow(("==>vboxNetAdpWinOidRequest\n")); + vboxNetCmnWinDumpOidRequest(__FUNCTION__, NdisRequest); + + switch (NdisRequest->RequestType) + { +#if 0 + case NdisRequestMethod: + Status = vboxNetAdpWinOidRqMethod(pAdapter, NdisRequest); + break; +#endif + + case NdisRequestSetInformation: + Status = vboxNetAdpWinOidRqSet(pAdapter, NdisRequest); + break; + + case NdisRequestQueryInformation: + case NdisRequestQueryStatistics: + Status = vboxNetAdpWinOidRqQuery(pAdapter, NdisRequest); + break; + + default: + Status = NDIS_STATUS_NOT_SUPPORTED; + break; + } + LogFlow(("<==vboxNetAdpWinOidRequest: status=0x%x\n", Status)); + return Status; +} + +DECLHIDDEN(VOID) vboxNetAdpWinSendNetBufferLists(IN NDIS_HANDLE MiniportAdapterContext, + IN PNET_BUFFER_LIST NetBufferLists, + IN NDIS_PORT_NUMBER PortNumber, + IN ULONG SendFlags) +{ + RT_NOREF1(PortNumber); + PVBOXNETADP_ADAPTER pAdapter = (PVBOXNETADP_ADAPTER)MiniportAdapterContext; + LogFlow(("==>vboxNetAdpWinSendNetBufferLists\n")); + PNET_BUFFER_LIST pNbl = NetBufferLists; + vboxNetAdpWinDumpPackets("vboxNetAdpWinSendNetBufferLists: got", pNbl); + + /* We alwast complete all send requests. */ + for (pNbl = NetBufferLists; pNbl; pNbl = NET_BUFFER_LIST_NEXT_NBL(pNbl)) + { + vboxNetAdpWinForwardToIntNet(pAdapter, pNbl, INTNETTRUNKDIR_HOST); + NET_BUFFER_LIST_STATUS(pNbl) = NDIS_STATUS_SUCCESS; + } + NdisMSendNetBufferListsComplete(pAdapter->hAdapter, NetBufferLists, + (SendFlags & NDIS_SEND_FLAGS_DISPATCH_LEVEL) ? + NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL : 0); + LogFlow(("<==vboxNetAdpWinSendNetBufferLists\n")); +} + +DECLHIDDEN(VOID) vboxNetAdpWinReturnNetBufferLists(IN NDIS_HANDLE MiniportAdapterContext, + IN PNET_BUFFER_LIST NetBufferLists, + IN ULONG ReturnFlags) +{ + LogFlow(("==>vboxNetAdpWinReturnNetBufferLists\n")); + RT_NOREF1(ReturnFlags); + PVBOXNETADP_ADAPTER pThis = (PVBOXNETADP_ADAPTER)MiniportAdapterContext; + PNET_BUFFER_LIST pList = NetBufferLists; + while (pList) + { + Assert(pList->SourceHandle == pThis->hAdapter); + Assert(NET_BUFFER_LIST_FIRST_NB(pList)); + Assert(NET_BUFFER_FIRST_MDL(NET_BUFFER_LIST_FIRST_NB(pList))); + + PNET_BUFFER_LIST pNextList = NET_BUFFER_LIST_NEXT_NBL(pList); + + vboxNetAdpWinFreeMdlChain(NET_BUFFER_FIRST_MDL(NET_BUFFER_LIST_FIRST_NB(pList))); + NdisFreeNetBufferList(pList); + Log4(("vboxNetLwfWinReturnNetBufferLists: freed NBL+NB+MDL+Data 0x%p\n", pList)); + Assert(ASMAtomicReadS32(&pThis->cBusy) > 0); + if (ASMAtomicDecS32(&pThis->cBusy) == 0) + NdisSetEvent(&pThis->EventIdle); + + pList = pNextList; + } + LogFlow(("<==vboxNetAdpWinReturnNetBufferLists\n")); +} + +DECLHIDDEN(VOID) vboxNetAdpWinCancelSend(IN NDIS_HANDLE MiniportAdapterContext, + IN PVOID CancelId) +{ + RT_NOREF2(MiniportAdapterContext, CancelId); + LogFlow(("==>vboxNetAdpWinCancelSend\n")); + Log(("vboxNetAdpWinCancelSend: We should not be here!\n")); + LogFlow(("<==vboxNetAdpWinCancelSend\n")); +} + + +DECLHIDDEN(BOOLEAN) vboxNetAdpWinCheckForHangEx(IN NDIS_HANDLE MiniportAdapterContext) +{ + RT_NOREF1(MiniportAdapterContext); + LogFlow(("==>vboxNetAdpWinCheckForHangEx\n")); + LogFlow(("<==vboxNetAdpWinCheckForHangEx return false\n")); + return FALSE; +} + +DECLHIDDEN(NDIS_STATUS) vboxNetAdpWinResetEx(IN NDIS_HANDLE MiniportAdapterContext, + OUT PBOOLEAN AddressingReset) +{ + RT_NOREF2(MiniportAdapterContext, AddressingReset); + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + LogFlow(("==>vboxNetAdpWinResetEx\n")); + LogFlow(("<==vboxNetAdpWinResetEx: status=0x%x\n", Status)); + return Status; +} + +DECLHIDDEN(VOID) vboxNetAdpWinDevicePnPEventNotify(IN NDIS_HANDLE MiniportAdapterContext, + IN PNET_DEVICE_PNP_EVENT NetDevicePnPEvent) +{ + RT_NOREF2(MiniportAdapterContext, NetDevicePnPEvent); + LogFlow(("==>vboxNetAdpWinDevicePnPEventNotify\n")); + Log(("vboxNetAdpWinDevicePnPEventNotify: PnP event=%d\n", NetDevicePnPEvent->DevicePnPEvent)); + LogFlow(("<==vboxNetAdpWinDevicePnPEventNotify\n")); +} + + +DECLHIDDEN(VOID) vboxNetAdpWinShutdownEx(IN NDIS_HANDLE MiniportAdapterContext, + IN NDIS_SHUTDOWN_ACTION ShutdownAction) +{ + RT_NOREF2(MiniportAdapterContext, ShutdownAction); + LogFlow(("==>vboxNetAdpWinShutdownEx\n")); + Log(("vboxNetAdpWinShutdownEx: action=%d\n", ShutdownAction)); + LogFlow(("<==vboxNetAdpWinShutdownEx\n")); +} + +DECLHIDDEN(VOID) vboxNetAdpWinCancelOidRequest(IN NDIS_HANDLE MiniportAdapterContext, + IN PVOID RequestId) +{ + RT_NOREF2(MiniportAdapterContext, RequestId); + LogFlow(("==>vboxNetAdpWinCancelOidRequest\n")); + Log(("vboxNetAdpWinCancelOidRequest: req id=%p\n", RequestId)); + LogFlow(("<==vboxNetAdpWinCancelOidRequest\n")); +} + + + +DECLHIDDEN(VOID) vboxNetAdpWinUnload(IN PDRIVER_OBJECT DriverObject) +{ + RT_NOREF1(DriverObject); + LogFlow(("==>vboxNetAdpWinUnload\n")); + PVBOXNETADPGLOBALS pGlobals = &g_VBoxNetAdpGlobals; + int rc; + NDIS_STATUS Status; + PKTHREAD pThread = NULL; + + /* We are about to disconnect IDC, let's make it clear so the factories will know */ + NdisAcquireSpinLock(&pGlobals->Lock); + uint32_t enmPrevState = ASMAtomicXchgU32(&g_VBoxNetAdpGlobals.enmIdcState, kVBoxNetAdpWinIdcState_Stopping); + NdisReleaseSpinLock(&pGlobals->Lock); + Log(("vboxNetAdpWinUnload: IDC state change %s -> Stopping\n", vboxNetAdpWinIdcStateToText(enmPrevState))); + + switch (enmPrevState) + { + case kVBoxNetAdpWinIdcState_Disconnected: + /* Have not even attempted to connect -- nothing to do. */ + break; + case kVBoxNetAdpWinIdcState_Stopping: + /* Impossible, but another thread is alreading doing StopIdc, bail out */ + LogError(("vboxNetAdpWinUnload: called in 'Stopping' state\n")); + break; + case kVBoxNetAdpWinIdcState_Connecting: + /* the worker thread is running, let's wait for it to stop */ + Status = ObReferenceObjectByHandle(g_VBoxNetAdpGlobals.hInitIdcThread, + THREAD_ALL_ACCESS, NULL, KernelMode, + (PVOID*)&pThread, NULL); + if (Status == STATUS_SUCCESS) + { + KeWaitForSingleObject(pThread, Executive, KernelMode, FALSE, NULL); + ObDereferenceObject(pThread); + } + else + { + LogError(("vboxNetAdpWinStopIdc: ObReferenceObjectByHandle(%p) failed with 0x%x\n", + g_VBoxNetAdpGlobals.hInitIdcThread, Status)); + } + break; + case kVBoxNetAdpWinIdcState_Connected: + /* the worker succeeded in IDC init and terminated */ + /* Make sure nobody uses the trunk factory. Wait half a second if needed. */ + if (!NdisWaitEvent(&pGlobals->EventUnloadAllowed, 500)) + LogRel(("VBoxNetAdp: unloading driver while trunk factory is in use!\n")); + rc = SUPR0IdcComponentDeregisterFactory(&pGlobals->SupDrvIDC, &pGlobals->SupDrvFactory); + AssertRC(rc); + SUPR0IdcClose(&pGlobals->SupDrvIDC); + Log(("vboxNetAdpWinUnload: closed IDC, rc=0x%x\n", rc)); + break; + } + if (pGlobals->hMiniportDriver) + NdisMDeregisterMiniportDriver(pGlobals->hMiniportDriver); + NdisFreeSpinLock(&pGlobals->Lock); + LogFlow(("<==vboxNetAdpWinUnload\n")); + RTLogDestroy(RTLogRelSetDefaultInstance(NULL)); + RTLogDestroy(RTLogSetDefaultInstance(NULL)); + RTR0Term(); +} + + +/** + * register the miniport driver + */ +DECLHIDDEN(NDIS_STATUS) vboxNetAdpWinRegister(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegistryPathStr) +{ + NDIS_MINIPORT_DRIVER_CHARACTERISTICS MChars; + + NdisZeroMemory(&MChars, sizeof (MChars)); + + MChars.Header.Type = NDIS_OBJECT_TYPE_MINIPORT_DRIVER_CHARACTERISTICS; + MChars.Header.Size = sizeof(NDIS_MINIPORT_DRIVER_CHARACTERISTICS); + MChars.Header.Revision = NDIS_MINIPORT_DRIVER_CHARACTERISTICS_REVISION_1; + + MChars.MajorNdisVersion = VBOXNETADP_VERSION_NDIS_MAJOR; + MChars.MinorNdisVersion = VBOXNETADP_VERSION_NDIS_MINOR; + + MChars.MajorDriverVersion = VBOXNETADP_VERSION_MAJOR; + MChars.MinorDriverVersion = VBOXNETADP_VERSION_MINOR; + + MChars.InitializeHandlerEx = vboxNetAdpWinInitializeEx; + MChars.HaltHandlerEx = vboxNetAdpWinHaltEx; + MChars.UnloadHandler = vboxNetAdpWinUnload; + MChars.PauseHandler = vboxNetAdpWinPause; + MChars.RestartHandler = vboxNetAdpWinRestart; + MChars.OidRequestHandler = vboxNetAdpWinOidRequest; + MChars.SendNetBufferListsHandler = vboxNetAdpWinSendNetBufferLists; + MChars.ReturnNetBufferListsHandler = vboxNetAdpWinReturnNetBufferLists; + MChars.CancelSendHandler = vboxNetAdpWinCancelSend; + MChars.CheckForHangHandlerEx = vboxNetAdpWinCheckForHangEx; + MChars.ResetHandlerEx = vboxNetAdpWinResetEx; + MChars.DevicePnPEventNotifyHandler = vboxNetAdpWinDevicePnPEventNotify; + MChars.ShutdownHandlerEx = vboxNetAdpWinShutdownEx; + MChars.CancelOidRequestHandler = vboxNetAdpWinCancelOidRequest; + + NDIS_STATUS Status; + g_VBoxNetAdpGlobals.hMiniportDriver = NULL; + Log(("vboxNetAdpWinRegister: registering miniport driver...\n")); + Status = NdisMRegisterMiniportDriver(pDriverObject, + pRegistryPathStr, + (NDIS_HANDLE)&g_VBoxNetAdpGlobals, + &MChars, + &g_VBoxNetAdpGlobals.hMiniportDriver); + Assert(Status == STATUS_SUCCESS); + if (Status == STATUS_SUCCESS) + { + Log(("vboxNetAdpWinRegister: successfully registered miniport driver; registering device...\n")); + } + else + { + Log(("ERROR! vboxNetAdpWinRegister: failed to register miniport driver, status=0x%x", Status)); + } + return Status; +} + + +RT_C_DECLS_BEGIN + +NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING pRegistryPath); + +RT_C_DECLS_END + +NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING pRegistryPath) +{ + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + int rc; + + + rc = RTR0Init(0); + AssertRC(rc); + if (RT_SUCCESS(rc)) + { + NdisZeroMemory(&g_VBoxNetAdpGlobals, sizeof (g_VBoxNetAdpGlobals)); + RTListInit(&g_VBoxNetAdpGlobals.ListOfAdapters); + NdisAllocateSpinLock(&g_VBoxNetAdpGlobals.Lock); + NdisInitializeEvent(&g_VBoxNetAdpGlobals.EventUnloadAllowed); + //g_VBoxNetAdpGlobals.PMCaps.WakeUpCapabilities.Flags = NDIS_DEVICE_WAKE_UP_ENABLE; + g_VBoxNetAdpGlobals.PMCaps.WakeUpCapabilities.MinMagicPacketWakeUp = NdisDeviceStateUnspecified; + g_VBoxNetAdpGlobals.PMCaps.WakeUpCapabilities.MinPatternWakeUp = NdisDeviceStateUnspecified; + + /* Initialize SupDrv interface */ + g_VBoxNetAdpGlobals.SupDrvFactory.pfnQueryFactoryInterface = vboxNetAdpWinQueryFactoryInterface; + memcpy(g_VBoxNetAdpGlobals.SupDrvFactory.szName, "VBoxNetAdp", sizeof("VBoxNetAdp")); + /* Initialize trunk factory interface */ + g_VBoxNetAdpGlobals.TrunkFactory.pfnRelease = vboxNetAdpWinFactoryRelease; + g_VBoxNetAdpGlobals.TrunkFactory.pfnCreateAndConnect = vboxNetAdpWinFactoryCreateAndConnect; + + rc = vboxNetAdpWinStartInitIdcThread(&g_VBoxNetAdpGlobals); + if (RT_SUCCESS(rc)) + { + Status = vboxNetAdpWinRegister(pDriverObject, pRegistryPath); + Assert(Status == STATUS_SUCCESS); + if (Status == NDIS_STATUS_SUCCESS) + { + Log(("NETADP: started successfully\n")); + return STATUS_SUCCESS; + } + } + else + Status = NDIS_STATUS_FAILURE; + NdisFreeSpinLock(&g_VBoxNetAdpGlobals.Lock); + RTLogDestroy(RTLogRelSetDefaultInstance(NULL)); + RTLogDestroy(RTLogSetDefaultInstance(NULL)); + + RTR0Term(); + } + else + { + Status = NDIS_STATUS_FAILURE; + } + + return Status; +} + diff --git a/src/VBox/HostDrivers/VBoxNetAdp/win/VBoxNetAdp-win.h b/src/VBox/HostDrivers/VBoxNetAdp/win/VBoxNetAdp-win.h new file mode 100644 index 00000000..8d9d15a5 --- /dev/null +++ b/src/VBox/HostDrivers/VBoxNetAdp/win/VBoxNetAdp-win.h @@ -0,0 +1,71 @@ +/* $Id: VBoxNetAdp-win.h $ */ +/** @file + * VBoxNetAdp-win.h - Host-only Miniport Driver, Windows-specific code. + */ +/* + * Copyright (C) 2014-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 . + * + * 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 + */ + +#ifndef VBOX_INCLUDED_SRC_VBoxNetAdp_win_VBoxNetAdp_win_h +#define VBOX_INCLUDED_SRC_VBoxNetAdp_win_VBoxNetAdp_win_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#define VBOXNETADP_VERSION_NDIS_MAJOR 6 +#define VBOXNETADP_VERSION_NDIS_MINOR 0 + +#define VBOXNETADP_VERSION_MAJOR 1 +#define VBOXNETADP_VERSION_MINOR 0 + +#define VBOXNETADP_VENDOR_NAME "Oracle" +#define VBOXNETADP_VENDOR_ID 0xFFFFFF +#define VBOXNETADP_MCAST_LIST_SIZE 32 +#define VBOXNETADP_MAX_FRAME_SIZE 1518 // TODO: 14+4+1500 + +//#define VBOXNETADP_NAME_UNIQUE L"{7af6b074-048d-4444-bfce-1ecc8bc5cb76}" +#define VBOXNETADP_NAME_SERVICE L"VBoxNetAdp" + +#define VBOXNETADP_NAME_LINK L"\\DosDevices\\Global\\VBoxNetAdp" +#define VBOXNETADP_NAME_DEVICE L"\\Device\\VBoxNetAdp" + +#define VBOXNETADPWIN_TAG 'ANBV' + +#define VBOXNETADPWIN_ATTR_FLAGS NDIS_MINIPORT_ATTRIBUTES_NDIS_WDM | NDIS_MINIPORT_ATTRIBUTES_NO_HALT_ON_SUSPEND +#define VBOXNETADP_MAC_OPTIONS NDIS_MAC_OPTION_NO_LOOPBACK +#define VBOXNETADP_SUPPORTED_FILTERS (NDIS_PACKET_TYPE_DIRECTED | \ + NDIS_PACKET_TYPE_MULTICAST | \ + NDIS_PACKET_TYPE_BROADCAST | \ + NDIS_PACKET_TYPE_PROMISCUOUS | \ + NDIS_PACKET_TYPE_ALL_MULTICAST) +#define VBOXNETADPWIN_SUPPORTED_STATISTICS 0 //TODO! +#define VBOXNETADPWIN_HANG_CHECK_TIME 4 + +#endif /* !VBOX_INCLUDED_SRC_VBoxNetAdp_win_VBoxNetAdp_win_h */ diff --git a/src/VBox/HostDrivers/VBoxNetAdp/win/VBoxNetAdp-win.rc b/src/VBox/HostDrivers/VBoxNetAdp/win/VBoxNetAdp-win.rc new file mode 100644 index 00000000..61cdb6e7 --- /dev/null +++ b/src/VBox/HostDrivers/VBoxNetAdp/win/VBoxNetAdp-win.rc @@ -0,0 +1,72 @@ +/* $Id: VBoxNetAdp-win.rc $ */ +/** @file + * VBoxNetAdp6 - Resource file containing version info and icon. + */ +/* + * Copyright (C) 2014-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 . + * + * 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 + */ + +#include +#include + +#define DESCRIPTION_STR "VirtualBox NDIS 6.0 Host-Only Network Adapter Driver\0" +#define FILENAME_STR "VBoxNetAdp6" + +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VBOX_RC_FILE_VERSION + PRODUCTVERSION VBOX_RC_FILE_VERSION + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK + FILEFLAGS VBOX_RC_FILE_FLAGS + FILEOS VBOX_RC_FILE_OS + FILETYPE VBOX_RC_TYPE_DRV + FILESUBTYPE VFT2_DRV_NETWORK +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "FileDescription", DESCRIPTION_STR + VALUE "InternalName", FILENAME_STR "\0" + VALUE "OriginalFilename", FILENAME_STR ".sys\0" + VALUE "CompanyName", VBOX_RC_COMPANY_NAME + VALUE "FileVersion", VBOX_RC_FILE_VERSION_STR + VALUE "LegalCopyright", VBOX_RC_LEGAL_COPYRIGHT + VALUE "ProductName", VBOX_RC_PRODUCT_NAME_STR + VALUE "ProductVersion", VBOX_RC_PRODUCT_VERSION_STR + VBOX_RC_MORE_STRINGS + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/src/VBox/HostDrivers/VBoxNetAdp/win/VBoxNetAdp6.inf b/src/VBox/HostDrivers/VBoxNetAdp/win/VBoxNetAdp6.inf new file mode 100644 index 00000000..6c18bf6a --- /dev/null +++ b/src/VBox/HostDrivers/VBoxNetAdp/win/VBoxNetAdp6.inf @@ -0,0 +1,101 @@ +; $Id: VBoxNetAdp6.inf $ +;; @file +; VBoxNetAdp6.inf - VirtualBox NDIS6 Miniport Driver inf file +; +; Note: We use the same component id as the old NetAdp implementation ? +; + +; +; Copyright (C) 2014-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 . +; +; 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 +; + +[Version] +Signature = "$Windows NT$" +;cat CatalogFile = VBoxNetAdp6.cat +Class = Net +ClassGUID = {4D36E972-E325-11CE-BFC1-08002BE10318} +Provider = %ORACLE% +;edit-DriverVer=10/23/2014,1.0.1.0 + + +[Manufacturer] +%ORACLE% = VBoxNetAdp6@COMMA-NT-ARCH@ + +[ControlFlags] + +[VBoxNetAdp6@DOT-NT-ARCH@] +%VBoxNetAdp6_Desc% = VBoxNetAdp6.ndi, sun_VBoxNetAdp + +[VBoxNetAdp6.ndi] +AddReg = VBoxNetAdp6.ndi.AddReg +Characteristics = 0x1 ; NCF_VIRTUAL +*IfType = 0x6 ; IF_TYPE_ETHERNET_CSMACD +*MediaType = 0x0 ; NdisMedium802_3 +*PhysicalMediaType = 14 ; NdisPhysicalMedium802_3 +CopyFiles = VBoxNetAdp6.Files.Sys + +[VBoxNetAdp6.ndi.Services] +AddService = VBoxNetAdp, 2, VBoxNetAdp6.AddService + +[VBoxNetAdp6.AddService] +DisplayName = %VBoxNetAdp6Service_Desc% +ServiceType = 1 ;SERVICE_KERNEL_DRIVER +StartType = 3 ;SERVICE_DEMAND_START +ErrorControl = 1 ;SERVICE_ERROR_NORMAL +ServiceBinary = %12%\VBoxNetAdp6.sys +LoadOrderGroup = NDIS + +[SourceDisksNames] +1=%DiskDescription%,"",, + +[SourceDisksFiles] +VBoxNetAdp6.sys=1 + +[DestinationDirs] +DefaultDestDir = 12 +VBoxNetAdp6.Files.Sys = 12 ; %windir%\System32\drivers + +[VBoxNetAdp6.Files.Sys] +VBoxNetAdp6.sys,,,2 + +[VBoxNetAdp6.ndi.AddReg] +HKR, , *NdisDeviceType, 0x00010001, 1 ; NDIS_DEVICE_TYPE_ENDPOINT +HKR, , BusNumber, 0, "0" +HKR, Ndi, Service, 0, VBoxNetAdp +HKR, Ndi, HelpText, , %VBoxNetAdp6_HELP% +HKR, Ndi\Interfaces, UpperRange, 0, ndis5 +HKR, Ndi\Interfaces, LowerRange, 0, ethernet + +[Strings] +ORACLE = "Oracle Corporation" +DiskDescription = "VirtualBox NDIS 6.0 Miniport Driver" +VBoxNetAdp6_Desc = "VirtualBox Host-Only Ethernet Adapter" +VBoxNetAdp6_HELP = "VirtualBox NDIS 6.0 Miniport Driver" +VBoxNetAdp6Service_Desc = "VirtualBox NDIS 6.0 Miniport Service" -- cgit v1.2.3