summaryrefslogtreecommitdiffstats
path: root/src/VBox/HostDrivers/VBoxNetFlt
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
commitf215e02bf85f68d3a6106c2a1f4f7f063f819064 (patch)
tree6bb5b92c046312c4e95ac2620b10ddf482d3fa8b /src/VBox/HostDrivers/VBoxNetFlt
parentInitial commit. (diff)
downloadvirtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.tar.xz
virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.zip
Adding upstream version 7.0.14-dfsg.upstream/7.0.14-dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/HostDrivers/VBoxNetFlt')
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/Makefile.kmk556
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/VBoxNetFlt.c1591
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/VBoxNetFlt.rc69
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/VBoxNetFltInternal.h499
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/darwin/Info.plist38
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/darwin/Makefile.kup0
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/darwin/VBoxNetFlt-darwin.cpp1754
-rwxr-xr-xsrc/VBox/HostDrivers/VBoxNetFlt/darwin/loadnetflt.sh133
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/freebsd/Makefile57
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/freebsd/VBoxNetFlt-freebsd.c817
-rwxr-xr-xsrc/VBox/HostDrivers/VBoxNetFlt/freebsd/files_vboxnetflt99
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/linux/Makefile81
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/linux/Makefile.kup0
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/linux/VBoxNetFlt-linux.c2609
-rwxr-xr-xsrc/VBox/HostDrivers/VBoxNetFlt/linux/files_vboxnetflt113
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/solaris/VBoxNetFlt-solaris.c4042
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/solaris/VBoxNetFltBow-solaris.c1702
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/solaris/vboxbow.conf43
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/solaris/vboxflt.conf56
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/win/Makefile.kup0
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/win/cfg/Makefile.kup0
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/win/cfg/VBoxNetCfg.cpp3770
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/win/drv/Makefile.kup0
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetAdp.inf95
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFlt-win.rc77
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFlt.inf117
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltCmn-win.h533
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltM-win.cpp1570
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltM-win.h67
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltM.inf82
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltP-win.cpp1595
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltP-win.h52
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltRt-win.cpp3650
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltRt-win.h972
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetLwf-win.cpp2736
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetLwf-win.h55
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetLwf.inf120
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/win/nobj/Makefile.kup0
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobj.cpp747
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobj.def42
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobj.h96
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobj.rc78
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobj.rgs13
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobjRc.h46
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobjT.idl55
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/win/tools/Makefile.kup0
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/win/tools/VBoxNetAdpInstall.cpp341
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/win/tools/VBoxNetAdpUninstall.cpp107
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/win/tools/VBoxNetFltInstall.cpp201
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/win/tools/VBoxNetFltUninstall.cpp133
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/win/tools/VBoxNetLwfInstall.cpp186
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/win/tools/VBoxNetLwfUninstall.cpp130
52 files changed, 31925 insertions, 0 deletions
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/Makefile.kmk b/src/VBox/HostDrivers/VBoxNetFlt/Makefile.kmk
new file mode 100644
index 00000000..e2704459
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/Makefile.kmk
@@ -0,0 +1,556 @@
+# $Id: Makefile.kmk $
+## @file
+# Sub-Makefile for the Network Filter Driver (VBoxNetFlt).
+#
+
+#
+# Copyright (C) 2008-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+SUB_DEPTH = ../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+
+if1of ($(KBUILD_TARGET), darwin win) # this ifeq must go, see @todo around elif
+ #
+ # VBoxNetFlt.sys - The mixed case driver.
+ #
+ ifdef VBOX_WITH_VBOXDRV
+ SYSMODS += VBoxNetFlt
+ endif
+ VBoxNetFlt_TEMPLATE = VBoxR0Drv
+ VBoxNetFlt_INST = $(INST_VBOXNETFLT)$(if $(eq $(KBUILD_TARGET),darwin),Contents/MacOS/)
+ VBoxNetFlt_DEBUG_INST.darwin = $(patsubst %/,%,$(INST_VBOXNETFLT))
+ if defined(VBOX_SIGNING_MODE) && "$(KBUILD_TARGET)" == "win"
+ VBoxNetFlt_INSTTYPE = none
+ VBoxNetFlt_DEBUG_INSTTYPE = both
+ endif
+ VBoxNetFlt_DEFS = IN_RT_R0 IN_SUP_STATIC
+ VBoxNetFlt_INCS = .
+ VBoxNetFlt_SOURCES.darwin = \
+ darwin/VBoxNetFlt-darwin.cpp
+ VBoxNetFlt_SDKS.win = ReorderCompilerIncs $(VBOX_WINDDK_WLH) $(VBOX_WINPSDK_INCS)
+ VBoxNetFlt_SOURCES.win = \
+ win/drv/VBoxNetFltP-win.cpp \
+ win/drv/VBoxNetFltM-win.cpp \
+ win/drv/VBoxNetFltRt-win.cpp \
+ win/drv/VBoxNetFlt-win.rc
+ # with WINDDKWLH the WIN9X_COMPAT_SPINLOCK is needed to avoid inline declaration of KeInitializeSpinLock
+ # otherwise the linker would complain about dumplicate _KeInitializeSpinLock@4 definition
+ # in ntoskrnl.lib and our object files
+ VBoxNetFlt_DEFS.win += WIN9X_COMPAT_SPINLOCK=1
+ VBoxNetFlt_DEFS.win += VBOXNETFLT_STATIC_CONFIG
+ VBoxNetFlt_DEFS.win += VBOXNETFLT_NO_PACKET_QUEUE
+ VBoxNetFlt_DEFS.win += NDIS_MINIPORT_DRIVER NDIS_WDM=1 BINARY_COMPATIBLE=0
+ VBoxNetFlt_DEFS.win += NDIS50_MINIPORT=1 NDIS50=1
+ ifdef VBOX_LOOPBACK_USEFLAGS
+ VBoxNetFlt_DEFS.win += VBOX_LOOPBACK_USEFLAGS
+ endif
+ VBoxNetFlt_SOURCES = VBoxNetFlt.c
+ #VBoxNetFlt_LDFLAGS.darwin = -v -Wl,-whyload -Wl,-v -Wl,-whatsloaded
+ VBoxNetFlt_LDFLAGS.win.x86 = -Entry:DriverEntry@8
+ VBoxNetFlt_LDFLAGS.win.amd64 = -Entry:DriverEntry
+ VBoxNetFlt_LIBS.win = \
+ $(PATH_SDK_$(VBOX_WINDDK_WLH)_LIB)/ntoskrnl.lib \
+ $(PATH_SDK_$(VBOX_WINDDK_WLH)_LIB)/hal.lib \
+ $(PATH_SDK_$(VBOX_WINDDK_WLH)_LIB)/ndis.lib \
+ $(PATH_SDK_$(VBOX_WINDDK_WLH)_LIB)/tdi.lib \
+ $(PATH_STAGE_LIB)/RuntimeR0Drv$(VBOX_SUFF_LIB)
+ VBoxNetFlt_LIBS = \
+ $(PATH_STAGE_LIB)/SUPR0IdcClient$(VBOX_SUFF_LIB)
+
+ darwin/VBoxNetFlt-darwin.cpp_DEFS = VBOX_SVN_REV=$(VBOX_SVN_REV)
+
+
+ # Darwin extras.
+ if "$(KBUILD_TARGET)" == "darwin" && defined(VBOX_WITH_VBOXDRV)
+ INSTALLS += VBoxNetFlt.kext
+ VBoxNetFlt.kext_INST = $(INST_VBOXNETFLT)Contents/
+ VBoxNetFlt.kext_SOURCES = $(VBoxNetFlt.kext_0_OUTDIR)/Contents/Info.plist
+ VBoxNetFlt.kext_CLEAN = $(VBoxNetFlt.kext_0_OUTDIR)/Contents/Info.plist
+ VBoxNetFlt.kext_BLDDIRS = $(VBoxNetFlt.kext_0_OUTDIR)/Contents/
+
+ $$(VBoxNetFlt.kext_0_OUTDIR)/Contents/Info.plist: $(PATH_SUB_CURRENT)/darwin/Info.plist $(VBOX_VERSION_MK) | $$(dir $$@)
+ $(call MSG_GENERATE,VBoxNetFlt,$@,$<)
+ $(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,VBoxNetFlt)
+
+ INSTALLS.darwin += Scripts-darwin
+ Scripts-darwin_INST = $(INST_DIST)
+ Scripts-darwin_EXEC_SOURCES = \
+ darwin/loadnetflt.sh
+ endif # darwin && host-drivers
+
+
+ ifeq ($(KBUILD_TARGET),win)
+ #
+ # Windows extras.
+ #
+ INSTALLS.win += VBoxNetFlt-inf
+ VBoxNetFlt-inf_TEMPLATE = VBoxR0DrvInfCat
+ VBoxNetFlt-inf_SOURCES = \
+ $(PATH_TARGET)/VBoxNetFltCat.dir/VBoxNetFlt.inf \
+ $(PATH_TARGET)/VBoxNetFltCat.dir/VBoxNetFltM.inf
+ VBoxNetFlt-inf_CLEAN = $(VBoxNetFlt-inf_SOURCES)
+ VBoxNetFlt-inf_BLDDIRS = $(PATH_TARGET)/VBoxNetFltCat.dir
+
+ $(PATH_TARGET)/VBoxNetFltCat.dir/VBoxNetFlt.inf: $(PATH_SUB_CURRENT)/win/drv/VBoxNetFlt.inf $(MAKEFILE_CURRENT) | $$(dir $$@)
+ $(call MSG_GENERATE,VBoxNetFlt-inf,$@,$<)
+ $(call VBOX_EDIT_INF_FN,$<,$@)
+
+ $(PATH_TARGET)/VBoxNetFltCat.dir/VBoxNetFltM.inf: $(PATH_SUB_CURRENT)/win/drv/VBoxNetFltM.inf $(MAKEFILE_CURRENT) | $$(dir $$@)
+ $(call MSG_GENERATE,VBoxNetFlt-inf,$@,$<)
+ $(call VBOX_EDIT_INF_FN,$<,$@)
+
+ ifdef VBOX_SIGNING_MODE
+ VBoxNetFlt-inf_SOURCES += \
+ $(PATH_TARGET)/VBoxNetFltCat.dir/VBoxNetFlt.sys \
+ $(PATH_TARGET)/VBoxNetFltCat.dir/VBoxNetFltNobj.dll \
+ $(PATH_TARGET)/VBoxNetFltCat.dir/VBoxNetFlt.cat
+
+ $(PATH_TARGET)/VBoxNetFltCat.dir/VBoxNetFlt.sys: $$(VBoxNetFlt_1_TARGET) | $$(dir $$@)
+ $(INSTALL) -m 644 $< $(@D)
+
+ $(PATH_TARGET)/VBoxNetFltCat.dir/VBoxNetFltNobj.dll: $$(VBoxNetFltNobj_1_TARGET) | $$(dir $$@)
+ $(INSTALL) -m 644 $< $(@D)
+
+ $(PATH_TARGET)/VBoxNetFltCat.dir/VBoxNetFlt.cat: \
+ $(PATH_TARGET)/VBoxNetFltCat.dir/VBoxNetFlt.sys \
+ $(PATH_TARGET)/VBoxNetFltCat.dir/VBoxNetFltNobj.dll \
+ $(PATH_TARGET)/VBoxNetFltCat.dir/VBoxNetFlt.inf \
+ $(PATH_TARGET)/VBoxNetFltCat.dir/VBoxNetFltM.inf
+ $(call MSG_TOOL,Inf2Cat,VBoxNetFlt-inf,$@,$<)
+ $(call VBOX_MAKE_CAT_FN, $(@D),$@)
+
+ endif # signing
+
+
+ #
+ # VBoxNetLwf.sys - The light-weight filter driver for NDIS6.
+ #
+ SYSMODS += VBoxNetLwf
+ VBoxNetLwf_TEMPLATE = VBoxR0Drv
+ VBoxNetLwf_INST = $(INST_VBOXNETFLT)
+ if defined(VBOX_SIGNING_MODE)
+ VBoxNetLwf_INSTTYPE = none
+ VBoxNetLwf_DEBUG_INSTTYPE = both
+ endif
+ VBoxNetLwf_DEFS = IN_RT_R0 IN_SUP_STATIC
+ VBoxNetLwf_INCS = .
+ VBoxNetLwf_SOURCES = VBoxNetFlt.c VBoxNetFlt.rc
+ VBoxNetLwf_SDKS.win = ReorderCompilerIncs $(VBOX_WINDDK_WLH) $(VBOX_WINPSDK_INCS)
+ VBoxNetLwf_SOURCES.win = \
+ win/drv/VBoxNetLwf-win.cpp
+ # with WINDDKWLH the WIN9X_COMPAT_SPINLOCK is needed to avoid inline declaration of KeInitializeSpinLock
+ # otherwise the linker would complain about dumplicate _KeInitializeSpinLock@4 definition
+ # in ntoskrnl.lib and our object files
+ VBoxNetLwf_DEFS.win += WIN9X_COMPAT_SPINLOCK=1 NDISLWF=1 DBG=1
+ # VBoxNetLwf_DEFS.win += VBOXNETFLT_STATIC_CONFIG
+ # VBoxNetLwf_DEFS.win += VBOXNETFLT_NO_PACKET_QUEUE
+ # VBoxNetLwf_DEFS.win += NDIS_MINIPORT_DRIVER NDIS_WDM=1 BINARY_COMPATIBLE=0
+ VBoxNetLwf_DEFS.win += NDIS60=1
+ # ifdef VBOX_LOOPBACK_USEFLAGS
+ # VBoxNetLwf_DEFS.win += VBOX_LOOPBACK_USEFLAGS
+ # endif
+ # VBoxNetLwf_SOURCES = VBoxNetLwf.c
+ VBoxNetLwf_LDFLAGS.win.x86 = -Entry:DriverEntry@8
+ VBoxNetLwf_LDFLAGS.win.amd64 = -Entry:DriverEntry
+ VBoxNetLwf_LIBS.win = \
+ $(PATH_SDK_$(VBOX_WINDDK_WLH)_LIB)/ntoskrnl.lib \
+ $(PATH_SDK_$(VBOX_WINDDK_WLH)_LIB)/hal.lib \
+ $(PATH_SDK_$(VBOX_WINDDK_WLH)_LIB)/ndis.lib \
+ $(PATH_SDK_$(VBOX_WINDDK_WLH)_LIB)/netio.lib \
+ $(PATH_STAGE_LIB)/RuntimeR0Drv$(VBOX_SUFF_LIB)
+ VBoxNetLwf_LIBS = \
+ $(PATH_STAGE_LIB)/SUPR0IdcClient$(VBOX_SUFF_LIB)
+ #
+ # VBoxNetLwf installation.
+ #
+ INSTALLS.win += VBoxNetLwf-inf
+ VBoxNetLwf-inf_TEMPLATE = VBoxR0DrvInfCat
+ VBoxNetLwf-inf_SOURCES = \
+ $(PATH_TARGET)/VBoxNetLwfCat.dir/VBoxNetLwf.inf
+ VBoxNetLwf-inf_CLEAN = $(VBoxNetLwf-inf_SOURCES)
+ VBoxNetLwf-inf_BLDDIRS = $(PATH_TARGET)/VBoxNetLwfCat.dir
+
+ $(PATH_TARGET)/VBoxNetLwfCat.dir/VBoxNetLwf.inf: $(PATH_SUB_CURRENT)/win/drv/VBoxNetLwf.inf $(MAKEFILE_CURRENT) | $$(dir $$@)
+ $(call MSG_GENERATE,VBoxNetLwf-inf,$@,$<)
+ $(call VBOX_EDIT_INF_FN,$<,$@)
+
+ ifdef VBOX_SIGNING_MODE
+ VBoxNetLwf-inf_SOURCES += \
+ $(PATH_TARGET)/VBoxNetLwfCat.dir/VBoxNetLwf.sys \
+ $(PATH_TARGET)/VBoxNetLwfCat.dir/VBoxNetLwf.cat \
+ $(PATH_TARGET)/VBoxNetLwfCat.dir/VBoxNetLwf.cat=>VBoxNetLwf-PreW10.cat
+
+ # $(PATH_TARGET)/VBoxNetLwfCat.dir/VBoxNetLwfNobj.dll \
+
+ $(PATH_TARGET)/VBoxNetLwfCat.dir/VBoxNetLwf.sys: $$(VBoxNetLwf_1_TARGET) | $$(dir $$@)
+ $(INSTALL) -m 644 $< $(@D)
+
+ #$(PATH_TARGET)/VBoxNetLwfCat.dir/VBoxNetLwfNobj.dll: $$(VBoxNetLwfNobj_1_TARGET) | $$(dir $$@)
+ # $(INSTALL) -m 644 $< $(@D)
+
+ $(PATH_TARGET)/VBoxNetLwfCat.dir/VBoxNetLwf.cat: \
+ $(PATH_TARGET)/VBoxNetLwfCat.dir/VBoxNetLwf.sys \
+ $(PATH_TARGET)/VBoxNetLwfCat.dir/VBoxNetLwf.inf
+ $(call MSG_TOOL,Inf2Cat,VBoxNetLwf-inf,$@,$<)
+ $(call VBOX_MAKE_CAT_FN, $(@D),$@)
+
+ endif # signing
+
+
+
+ #
+ # WinNetConfig - static library with host network interface config API (for the installer)
+ #
+ LIBRARIES.win += WinNetConfigSharedStatic
+ WinNetConfigSharedStatic_TEMPLATE = VBoxR3StaticDllNoAsan
+ WinNetConfigSharedStatic_SDKS = ReorderCompilerIncs $(VBOX_WINPSDK) $(VBOX_WINDDK)
+ WinNetConfigSharedStatic_DEFS = _UNICODE UNICODE
+ WinNetConfigSharedStatic_SOURCES = \
+ win/cfg/VBoxNetCfg.cpp \
+ ../../Main/glue/string.cpp
+
+ # Version for DLLs:
+ LIBRARIES.win += WinNetConfigDll
+ WinNetConfigDll_TEMPLATE = VBoxR3Dll
+ WinNetConfigDll_EXTENDS = WinNetConfigSharedStatic
+
+ # Version for EXEs:
+ LIBRARIES.win += WinNetConfigExe
+ WinNetConfigExe_TEMPLATE = VBoxR3Exe
+ WinNetConfigExe_EXTENDS = WinNetConfigSharedStatic
+
+
+ #
+ # Template for NetFltInstall and friends.
+ #
+ TEMPLATE_VBoxNetFltR3 = Template for NetFltInstall, NetFltUninstall, NetAdpInstall, NetAdpUninstall, ++
+ TEMPLATE_VBoxNetFltR3_EXTENDS = VBoxR3Exe
+ TEMPLATE_VBoxNetFltR3_SDKS = $(TEMPLATE_VBoxR3Exe_SDKS) ReorderCompilerIncs $(VBOX_WINPSDK) $(VBOX_WINDDK) VBoxNtDll VBoxWinNewDevLib
+ TEMPLATE_VBoxNetFltR3_LIBS = $(TEMPLATE_VBoxR3Exe_LIBS) \
+ $(WinNetConfigExe_1_TARGET) \
+ $(PATH_STAGE_LIB)/VBoxDrvCfgExe$(VBOX_SUFF_LIB) \
+ $(LIB_RUNTIME) \
+ $(PATH_TOOL_$(VBOX_VCC_TOOL)_LIB)/comsupp.lib \
+ $(PATH_SDK_$(VBOX_WINPSDK)_LIB)/WbemUuid.Lib
+
+ #
+ # NetFltInstall
+ #
+ PROGRAMS.win += NetFltInstall
+ NetFltInstall_TEMPLATE = VBoxNetFltR3
+ NetFltInstall_SOURCES = win/tools/VBoxNetFltInstall.cpp
+
+ #
+ # NetFltUninstall
+ #
+ PROGRAMS.win += NetFltUninstall
+ NetFltUninstall_TEMPLATE = VBoxNetFltR3
+ NetFltUninstall_SOURCES = win/tools/VBoxNetFltUninstall.cpp
+
+ #
+ # NetAdpInstall
+ #
+ PROGRAMS.win += NetAdpInstall
+ NetAdpInstall_TEMPLATE = VBoxNetFltR3
+ NetAdpInstall_SOURCES = win/tools/VBoxNetAdpInstall.cpp
+
+ #
+ # NetAdpUninstall
+ #
+ PROGRAMS.win += NetAdpUninstall
+ NetAdpUninstall_TEMPLATE = VBoxNetFltR3
+ NetAdpUninstall_SOURCES = win/tools/VBoxNetAdpUninstall.cpp
+
+ #
+ # NetAdp6Install
+ #
+ PROGRAMS.win += NetAdp6Install
+ NetAdp6Install_TEMPLATE = VBoxNetFltR3
+ NetAdp6Install_SOURCES = win/tools/VBoxNetAdpInstall.cpp
+ NetAdp6Install_DEFS = NDIS60=1
+
+ #
+ # NetAdp6Uninstall
+ #
+ PROGRAMS.win += NetAdp6Uninstall
+ NetAdp6Uninstall_TEMPLATE = VBoxNetFltR3
+ NetAdp6Uninstall_SOURCES = win/tools/VBoxNetAdpUninstall.cpp
+ NetAdp6Uninstall_DEFS = NDIS60=1
+
+ #
+ # NetLwfInstall
+ #
+ PROGRAMS.win += NetLwfInstall
+ NetLwfInstall_TEMPLATE = VBoxNetFltR3
+ NetLwfInstall_SOURCES = win/tools/VBoxNetLwfInstall.cpp
+
+ #
+ # NetLwfUninstall
+ #
+ PROGRAMS.win += NetLwfUninstall
+ NetLwfUninstall_TEMPLATE = VBoxNetFltR3
+ NetLwfUninstall_SOURCES = win/tools/VBoxNetLwfUninstall.cpp
+
+ #
+ # VBoxNetFltNobj
+ #
+ DLLS.win += VBoxNetFltNobj
+ VBoxNetFltNobj_TEMPLATE = VBoxR3StaticDll
+ if defined(VBOX_SIGNING_MODE)
+ VBoxNetFltNobj_INSTTYPE = none
+ VBoxNetFltNobj_DEBUG_INSTTYPE = both
+ endif
+ VBoxNetFltNobj_SDKS = ReorderCompilerIncs $(VBOX_WINPSDK) $(VBOX_WINDDK)
+ VBoxNetFltNobj_DEFS = WIN32 _ATL_STATIC_REGISTRY
+ VBoxNetFltNobj_INCS = \
+ $(VBoxNetFltNobj_0_OUTDIR)
+ VBoxNetFltNobj_SOURCES = \
+ win/nobj/VBoxNetFltNobj.cpp \
+ win/nobj/VBoxNetFltNobj.def \
+ win/nobj/VBoxNetFltNobj.rc
+ #VBoxNetFltNobj_INTERMEDIATES =
+ VBoxNetFltNobj_DEPS = \
+ $(VBoxNetFltNobj_0_OUTDIR)/VBoxNetFltNobjT_i.c \
+ $(VBoxNetFltNobj_0_OUTDIR)/VBoxNetFltNobjT_p.c \
+ $(VBoxNetFltNobj_0_OUTDIR)/VBoxNetFltNobjT.h \
+ $(VBoxNetFltNobj_0_OUTDIR)/dlldata.c \
+ $(VBoxNetFltNobj_0_OUTDIR)/VBoxNetFltNobjT.tlb
+ VBoxNetFltNobj_CLEAN = $(VBoxNetFltNobj_DEPS)
+
+ $$(VBoxNetFltNobj_0_OUTDIR)/VBoxNetFltNobjT_i.c \
+ + $$(VBoxNetFltNobj_0_OUTDIR)/VBoxNetFltNobjT_p.c \
+ + $$(VBoxNetFltNobj_0_OUTDIR)/VBoxNetFltNobjT.h \
+ + $$(VBoxNetFltNobj_0_OUTDIR)/dlldata.c \
+ + $$(VBoxNetFltNobj_0_OUTDIR)/VBoxNetFltNobjT.tlb: \
+ $(PATH_SUB_CURRENT)/win/nobj/VBoxNetFltNobjT.idl \
+ | $$(dir $$@)
+ $(VBOX_MIDL_REDIRECT) $(VBOX_WIN_MIDL) /nologo \
+ /out $(call VBOX_FN_MAKE_WIN_PATH,$(VBoxNetFltNobj_0_OUTDIR)) \
+ /cpp_cmd $(VBOX_MIDL_CPP_CMD) \
+ $(qforeachfile unq, incdir, $(SDK_$(VBOX_WINDDK)_INCS) $(SDK_$(VBOX_WINPSDK)_INCS),/I $(quote-sh $(incdir))) \
+ $(call VBOX_FN_MAKE_WIN_PATH,$<)
+ $(call def_VBoxMidlOutputDisableMscWarnings,$(VBoxNetFltNobj_0_OUTDIR)/VBoxNetFltNobjT.h)
+ $(call def_VBoxMidlOutputDisableMscWarnings,$(VBoxNetFltNobj_0_OUTDIR)/VBoxNetFltNobjT_i.c)
+ $(call def_VBoxMidlOutputDisableMscWarnings,$(VBoxNetFltNobj_0_OUTDIR)/VBoxNetFltNobjT_p.c)
+
+
+ #
+ # VBoxNetAdp.sys - The VirtualBox Adapter miniport driver.
+ #
+ SYSMODS.win += VBoxNetAdp
+ VBoxNetAdp_TEMPLATE = VBoxR0Drv
+ #VBoxNetAdp_INST = $(INST_VBOXNETADP)
+ if defined(VBOX_SIGNING_MODE)
+ VBoxNetAdp_INSTTYPE.win = none
+ VBoxNetAdp_DEBUG_INSTTYPE.win = both
+ endif
+ VBoxNetAdp_DEFS = IN_RT_R0 IN_SUP_STATIC
+ VBoxNetAdp_INCS := $(PATH_SUB_CURRENT)
+ VBoxNetAdp_SDKS = ReorderCompilerIncs $(VBOX_WINDDK_WLH) $(VBOX_WINPSDK_INCS)
+ VBoxNetAdp_SOURCES = \
+ VBoxNetFlt.c \
+ win/drv/VBoxNetFltM-win.cpp \
+ win/drv/VBoxNetFltRt-win.cpp \
+ win/drv/VBoxNetFlt-win.rc
+ VBoxNetAdp_DEFS += VBOXNETFLT_STATIC_CONFIG VBOXNETADP
+ VBoxNetAdp_DEFS.win += VBOXNETFLT_NO_PACKET_QUEUE
+ VBoxNetAdp_DEFS += NDIS_MINIPORT_DRIVER NDIS_WDM=1 BINARY_COMPATIBLE=0
+ VBoxNetAdp_DEFS += NDIS50_MINIPORT=1 NDIS50=1
+ VBoxNetAdp_LDFLAGS.win.x86 = -Entry:DriverEntry@8
+ VBoxNetAdp_LDFLAGS.win.amd64 = -Entry:DriverEntry
+ VBoxNetAdp_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)
+ VBoxNetAdp_LIBS = \
+ $(PATH_STAGE_LIB)/SUPR0IdcClient$(VBOX_SUFF_LIB)
+
+
+ INSTALLS.win += VBoxNetAdp-inf
+ VBoxNetAdp-inf_TEMPLATE = VBoxR0DrvInfCat
+ VBoxNetAdp-inf_SOURCES = \
+ $(PATH_TARGET)/VBoxNetAdpCat.dir/VBoxNetAdp.inf
+ VBoxNetAdp-inf_CLEAN = $(VBoxNetAdp-inf_SOURCES)
+ VBoxNetAdp-inf_BLDDIRS = $(PATH_TARGET)/VBoxNetAdpCat.dir
+
+ $(PATH_TARGET)/VBoxNetAdpCat.dir/VBoxNetAdp.inf: $(PATH_SUB_CURRENT)/win/drv/VBoxNetAdp.inf $(MAKEFILE_CURRENT) | $$(dir $$@)
+ $(call MSG_GENERATE,VBoxNetAdp-inf,$@,$<)
+ $(call VBOX_EDIT_INF_FN,$<,$@)
+
+ ifdef VBOX_SIGNING_MODE
+ VBoxNetAdp-inf_SOURCES += \
+ $(PATH_TARGET)/VBoxNetAdpCat.dir/VBoxNetAdp.sys \
+ $(PATH_TARGET)/VBoxNetAdpCat.dir/VBoxNetAdp.cat
+
+ $(PATH_TARGET)/VBoxNetAdpCat.dir/VBoxNetAdp.sys: $$(VBoxNetAdp_1_TARGET) | $$(dir $$@)
+ $(INSTALL) -m 644 $< $(@D)
+
+ $(PATH_TARGET)/VBoxNetAdpCat.dir/VBoxNetAdp.cat: \
+ $(PATH_TARGET)/VBoxNetAdpCat.dir/VBoxNetAdp.sys \
+ $(PATH_TARGET)/VBoxNetAdpCat.dir/VBoxNetAdp.inf
+ $(call MSG_TOOL,Inf2Cat,VBoxNetFlt-inf,$@,$<)
+ $(call VBOX_MAKE_CAT_FN, $(@D),$@)
+
+ endif # ifdef VBOX_SIGNING_MODE
+
+ endif #ifeq ($(KBUILD_TARGET), win)
+
+else if1of ($(KBUILD_TARGET), solaris freebsd) ## @todo merge this with the mixed case stuff.
+ #
+ # vboxnetflt(.ko/.o/) - The lower case driver.
+ # Note! On Solaris the name has to be <= 8 chars long.
+ # The DEBUG_HASH* stuff is for CONFIG_DYNAMIC_DEBUG-enabled kernels
+ #
+ ifdef VBOX_WITH_VBOXDRV
+ SYSMODS += vboxnetflt
+ vboxnetflt_TEMPLATE = VBoxR0Drv
+ vboxnetflt_NAME.solaris = vboxflt
+ vboxnetflt_DEFS = IN_RT_R0
+ vboxnetflt_DEPS.solaris += $(VBOX_SVN_REV_KMK)
+ vboxnetflt_INCS := \
+ $(PATH_SUB_CURRENT)
+ vboxnetflt_LDFLAGS.solaris += -N drv/vboxdrv -N misc/ctf
+ vboxnetflt_LIBS = \
+ $(PATH_STAGE_LIB)/SUPR0IdcClient$(VBOX_SUFF_LIB)
+ ## @todo vboxflt should resolves all the IPRT bits from vboxdrv.
+ #vboxnetflt_LIBS += \
+ # $(PATH_STAGE_LIB)/RuntimeR0Drv$(VBOX_SUFF_LIB)
+ vboxnetflt_SOURCES.solaris = solaris/VBoxNetFlt-solaris.c
+ vboxnetflt_SOURCES.freebsd = freebsd/VBoxNetFlt-freebsd.c
+ vboxnetflt_SOURCES = VBoxNetFlt.c
+ solaris/VBoxNetFlt-solaris.c_DEFS = VBOX_SVN_REV=$(VBOX_SVN_REV)
+
+ ifdef VBOX_WITH_NETFLT_CROSSBOW
+ SYSMODS += vboxnetbow
+ vboxnetbow_TEMPLATE = VBoxR0Drv
+ vboxnetbow_NAME = vboxbow
+ vboxnetbow_DEFS = vboxnetflt_DEFS VBOX_WITH_NETFLT_CROSSBOW
+ vboxnetbow_INCS := $(PATH_SUB_CURRENT)
+ vboxnetbow_LDFLAGS += -N drv/vboxdrv -N drv/vnic -N misc/mac -N misc/dls
+ vboxnetbow_LIBS = \
+ $(PATH_STAGE_LIB)/SUPR0IdcClient$(VBOX_SUFF_LIB)
+ vboxnetbow_SOURCES.solaris = solaris/VBoxNetFltBow-solaris.c
+ vboxnetbow_SOURCES = VBoxNetFlt.c
+ solaris/VBoxNetFltBow-solaris.c_DEFS = VBOX_SVN_REV=$(VBOX_SVN_REV)
+ endif # VBOX_WITH_NETFLT_CROSSBOW
+ endif # VBOX_WITH_VBOXDRV
+endif # to be removed.
+
+
+ifeq ($(KBUILD_TARGET),linux)
+ #
+ # Install source files for compilation on Linux.
+ # files_vboxnetflt defines VBOX_VBOXNETFLT_SOURCES.
+ #
+ include $(PATH_SUB_CURRENT)/linux/files_vboxnetflt
+ INSTALLS += VBoxNetFlt-src VBoxNetFlt-sh
+ VBoxNetFlt-src_INST = bin/src/vboxnetflt/
+ VBoxNetFlt-src_SOURCES = \
+ $(subst $(DQUOTE),,$(VBOX_VBOXNETFLT_SOURCES)) \
+ $(VBoxNetFlt-src_0_OUTDIR)/Makefile
+ VBoxNetFlt-src_CLEAN = \
+ $(VBoxNetFlt-src_0_OUTDIR)/Makefile \
+ $(PATH_TARGET)/VBoxNetFlt-src-1.dep
+
+ # Scripts needed for building the kernel module.
+
+ includedep $(PATH_TARGET)/VBoxNetFlt-src-1.dep
+ $$(VBoxNetFlt-src_0_OUTDIR)/Makefile: \
+ $(PATH_SUB_CURRENT)/linux/Makefile \
+ $$(if $$(eq $$(VBoxNetFlt/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)$(RM) -f -- $(PATH_TARGET)/VBoxNetFlt-src-1.dep
+ %$(QUIET2)$(APPEND) '$(PATH_TARGET)/VBoxNetFlt-src-1.dep' 'VBoxNetFlt/linux/Makefile_VBOX_HARDENED=$(VBOX_WITH_HARDENING)'
+
+ #
+ # Build test for the linux host kernel modules.
+ #
+ $(evalcall2 VBOX_LINUX_KMOD_TEST_BUILD_RULE_FN,VBoxNetFlt-src,vboxdrv-src,)
+endif # linux
+
+# Gross hack for FreeBSD 7, should figure this out properly
+## @todo Properly generate opt_netgraph.h
+ifeq ($(KBUILD_TARGET),freebsd)
+ vboxnetflt_DEPS.freebsd += $(PATH_OUT)/opt_netgraph.h
+ $(PATH_OUT)/opt_netgraph.h:
+ echo > $(PATH_OUT)/opt_netgraph.h
+
+ #
+ # Install source files for compilation on FreeBSD.
+ # files_vboxnetflt defines VBOX_VBOXNETFLT_SOURCES.
+ #
+ include $(PATH_SUB_CURRENT)/freebsd/files_vboxnetflt
+ INSTALLS += VBoxNetFlt-src
+ VBoxNetFlt-src_INST = bin/src/vboxnetflt/
+ VBoxNetFlt-src_SOURCES = \
+ $(subst $(DQUOTE),,$(VBOX_VBOXNETFLT_SOURCES)) \
+ $(VBoxNetFlt-src_0_OUTDIR)/Makefile
+ VBoxNetFlt-src_CLEAN = \
+ $(VBoxNetFlt-src_0_OUTDIR)/Makefile
+
+ $$(VBoxNetFlt-src_0_OUTDIR)/Makefile: \
+ $(PATH_SUB_CURRENT)/freebsd/Makefile \
+ $$(if $$(eq $$(VBoxNetFlt/freebsd/Makefile_VBOX_HARDENED),$$(VBOX_WITH_HARDENING)),,FORCE) \
+ | $$(dir $$@)
+ $(QUIET)$(RM) -f -- $@
+ $(QUIET)$(CP) -f $< $@
+ ifndef VBOX_WITH_HARDENING
+ $(QUIET)$(SED) -e "s;VBOX_WITH_HARDENING;;g" --output $@.tmp $@
+ ${QUIET}$(MV) -f $@.tmp $@
+ endif
+ ifndef VBOX_WITH_NETFLT_VIMAGE
+ $(QUIET)$(SED) -e "s;-DVIMAGE;;g" --output $@.tmp $@
+ ${QUIET}$(MV) -f $@.tmp $@
+ endif
+endif # freebsd
+
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/VBoxNetFlt.c b/src/VBox/HostDrivers/VBoxNetFlt/VBoxNetFlt.c
new file mode 100644
index 00000000..b80af59e
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/VBoxNetFlt.c
@@ -0,0 +1,1591 @@
+/* $Id: VBoxNetFlt.c $ */
+/** @file
+ * VBoxNetFlt - Network Filter 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 <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+/** @page pg_netflt VBoxNetFlt - Network Interface Filter
+ *
+ * This is a kernel module that attaches to a real interface on the host and
+ * filters and injects packets.
+ *
+ * In the big picture we're one of the three trunk interface on the internal
+ * network, the one named "NIC Filter Driver": @image html Networking_Overview.gif
+ *
+ *
+ * @section sec_netflt_locking Locking and Potential Races
+ *
+ * The main challenge here is to make sure the netfilter and internal network
+ * instances won't be destroyed while someone is calling into them.
+ *
+ * The main calls into or out of of the filter driver are:
+ * - Send.
+ * - Async send completion (not implemented yet)
+ * - Release by the internal network.
+ * - Receive.
+ * - Disappearance of the host networking interface.
+ * - Reappearance of the host networking interface.
+ *
+ * The latter two calls are can be caused by driver unloading/loading or the
+ * device being physical unplugged (e.g. a USB network device). Actually, the
+ * unload scenario must fervently be prevent as it will cause panics because the
+ * internal network will assume the trunk is around until it releases it.
+ * @todo Need to figure which host allow unloading and block/fix it.
+ *
+ * Currently the netfilter instance lives until the internal network releases
+ * it. So, it is the internal networks responsibility to make sure there are no
+ * active calls when it releases the trunk and destroys the network. The
+ * netfilter assists in this by providing INTNETTRUNKIFPORT::pfnSetState and
+ * INTNETTRUNKIFPORT::pfnWaitForIdle. The trunk state is used to enable/disable
+ * promiscuous mode on the hardware NIC (or similar activation) as well
+ * indicating that disconnect is imminent and no further calls shall be made
+ * into the internal network. After changing the state to disconnecting and
+ * prior to invoking INTNETTRUNKIFPORT::pfnDisconnectAndRelease, the internal
+ * network will use INTNETTRUNKIFPORT::pfnWaitForIdle to wait for any still
+ * active calls to complete.
+ *
+ * The netfilter employs a busy counter and an internal state in addition to the
+ * public trunk state. All these variables are protected using a spinlock.
+ *
+ *
+ * @section sec_netflt_msc Locking / Sequence Diagrams - OBSOLETE
+ *
+ * !OBSOLETE! - THIS WAS THE OLD APPROACH!
+ *
+ * This secion contains a few sequence diagrams describing the problematic
+ * transitions of a host interface filter instance.
+ *
+ * The thing that makes it all a bit problematic is that multiple events may
+ * happen at the same time, and that we have to be very careful to avoid
+ * deadlocks caused by mixing our locks with the ones in the host kernel. The
+ * main events are receive, send, async send completion, disappearance of the
+ * host networking interface and its reappearance. The latter two events are
+ * can be caused by driver unloading/loading or the device being physical
+ * unplugged (e.g. a USB network device).
+ *
+ * The strategy for dealing with these issues are:
+ * - Use a simple state machine.
+ * - Require the user (IntNet) to serialize all its calls to us,
+ * while at the same time not owning any lock used by any of the
+ * the callbacks we might call on receive and async send completion.
+ * - Make sure we're 100% idle before disconnecting, and have a
+ * disconnected status on both sides to fend off async calls.
+ * - Protect the host specific interface handle and the state variables
+ * using a spinlock.
+ *
+ *
+ * @subsection subsec_netflt_msc_dis_rel Disconnect from the network and release - OBSOLETE
+ *
+ * @msc
+ * VM, IntNet, NetFlt, Kernel, Wire;
+ *
+ * VM->IntNet [label="pkt0", linecolor="green", textcolor="green"];
+ * IntNet=>IntNet [label="Lock Network", linecolor="green", textcolor="green" ];
+ * IntNet=>IntNet [label="Route packet -> wire", linecolor="green", textcolor="green" ];
+ * IntNet=>IntNet [label="Unlock Network", linecolor="green", textcolor="green" ];
+ * IntNet=>NetFlt [label="pkt0 to wire", linecolor="green", textcolor="green" ];
+ * NetFlt=>Kernel [label="pkt0 to wire", linecolor="green", textcolor="green"];
+ * Kernel->Wire [label="pkt0 to wire", linecolor="green", textcolor="green"];
+ *
+ * --- [label="Suspending the trunk interface"];
+ * IntNet=>IntNet [label="Lock Network"];
+ *
+ * Wire->Kernel [label="pkt1 - racing us", linecolor="red", textcolor="red"];
+ * Kernel=>>NetFlt [label="pkt1 - racing us", linecolor="red", textcolor="red"];
+ * NetFlt=>>IntNet [label="pkt1 recv - blocks", linecolor="red", textcolor="red"];
+ *
+ * IntNet=>IntNet [label="Mark Trunk Suspended"];
+ * IntNet=>IntNet [label="Unlock Network"];
+ *
+ * IntNet=>NetFlt [label="pfnSetActive(false)"];
+ * NetFlt=>NetFlt [label="Mark inactive (atomic)"];
+ * IntNet<<NetFlt;
+ * IntNet=>NetFlt [label="pfnWaitForIdle(forever)"];
+ *
+ * IntNet=>>NetFlt [label="pkt1 to host", linecolor="red", textcolor="red"];
+ * NetFlt=>>Kernel [label="pkt1 to host", linecolor="red", textcolor="red"];
+ *
+ * Kernel<-Wire [label="pkt0 on wire", linecolor="green", textcolor="green"];
+ * NetFlt<<Kernel [label="pkt0 on wire", linecolor="green", textcolor="green"];
+ * IntNet<<=NetFlt [label="pfnSGRelease", linecolor="green", textcolor="green"];
+ * IntNet<<=IntNet [label="Lock Net, free SG, Unlock Net", linecolor="green", textcolor="green"];
+ * IntNet>>NetFlt [label="pfnSGRelease", linecolor="green", textcolor="green"];
+ * NetFlt<-NetFlt [label="idle", linecolor="green", textcolor="green"];
+ *
+ * IntNet<<NetFlt [label="idle (pfnWaitForIdle)"];
+ *
+ * Wire->Kernel [label="pkt2", linecolor="red", textcolor="red"];
+ * Kernel=>>NetFlt [label="pkt2", linecolor="red", textcolor="red"];
+ * NetFlt=>>Kernel [label="pkt2 to host", linecolor="red", textcolor="red"];
+ *
+ * VM->IntNet [label="pkt3", linecolor="green", textcolor="green"];
+ * IntNet=>IntNet [label="Lock Network", linecolor="green", textcolor="green" ];
+ * IntNet=>IntNet [label="Route packet -> drop", linecolor="green", textcolor="green" ];
+ * IntNet=>IntNet [label="Unlock Network", linecolor="green", textcolor="green" ];
+ *
+ * --- [label="The trunk interface is idle now, disconnect it"];
+ * IntNet=>IntNet [label="Lock Network"];
+ * IntNet=>IntNet [label="Unlink Trunk"];
+ * IntNet=>IntNet [label="Unlock Network"];
+ * IntNet=>NetFlt [label="pfnDisconnectAndRelease"];
+ * NetFlt=>Kernel [label="iflt_detach"];
+ * NetFlt<<=Kernel [label="iff_detached"];
+ * NetFlt>>Kernel [label="iff_detached"];
+ * NetFlt<<Kernel [label="iflt_detach"];
+ * NetFlt=>NetFlt [label="Release"];
+ * IntNet<<NetFlt [label="pfnDisconnectAndRelease"];
+ *
+ * @endmsc
+ *
+ *
+ *
+ * @subsection subsec_netflt_msc_hif_rm Host Interface Removal - OBSOLETE
+ *
+ * The ifnet_t (pIf) is a tricky customer as any reference to it can potentially
+ * race the filter detaching. The simple way of solving it on Darwin is to guard
+ * all access to the pIf member with a spinlock. The other host systems will
+ * probably have similar race conditions, so the spinlock is a generic thing.
+ *
+ * @msc
+ * VM, IntNet, NetFlt, Kernel;
+ *
+ * VM->IntNet [label="pkt0", linecolor="green", textcolor="green"];
+ * IntNet=>IntNet [label="Lock Network", linecolor="green", textcolor="green" ];
+ * IntNet=>IntNet [label="Route packet -> wire", linecolor="green", textcolor="green" ];
+ * IntNet=>IntNet [label="Unlock Network", linecolor="green", textcolor="green" ];
+ * IntNet=>NetFlt [label="pkt0 to wire", linecolor="green", textcolor="green" ];
+ * NetFlt=>Kernel [label="ifnet_reference w/ spinlock", linecolor="green", textcolor="green" ];
+ * NetFlt<<Kernel [label="ifnet_reference", linecolor="green", textcolor="green" ];
+ * NetFlt=>Kernel [label="pkt0 to wire (blocks)", linecolor="green", textcolor="green" ];
+ *
+ * --- [label="The host interface is being disconnected"];
+ * Kernel->NetFlt [label="iff_detached"];
+ * NetFlt=>Kernel [label="ifnet_release w/ spinlock"];
+ * NetFlt<<Kernel [label="ifnet_release"];
+ * NetFlt=>NetFlt [label="fDisconnectedFromHost=true"];
+ * NetFlt>>Kernel [label="iff_detached"];
+ *
+ * NetFlt<<Kernel [label="dropped", linecolor="green", textcolor="green"];
+ * NetFlt=>NetFlt [label="Acquire spinlock", linecolor="green", textcolor="green"];
+ * NetFlt=>Kernel [label="ifnet_release", linecolor="green", textcolor="green"];
+ * NetFlt<<Kernel [label="ifnet_release", linecolor="green", textcolor="green"];
+ * NetFlt=>NetFlt [label="pIf=NULL", linecolor="green", textcolor="green"];
+ * NetFlt=>NetFlt [label="Release spinlock", linecolor="green", textcolor="green"];
+ * IntNet<=NetFlt [label="pfnSGRelease", linecolor="green", textcolor="green"];
+ * IntNet>>NetFlt [label="pfnSGRelease", linecolor="green", textcolor="green"];
+ * IntNet<<NetFlt [label="pkt0 to wire", linecolor="green", textcolor="green"];
+ *
+ * @endmsc
+ *
+ *
+ *
+ * @subsection subsec_netflt_msc_hif_rd Host Interface Rediscovery - OBSOLETE
+ *
+ * The rediscovery is performed when we receive a send request and a certain
+ * period have elapsed since the last attempt, i.e. we're polling it. We
+ * synchronize the rediscovery with disconnection from the internal network
+ * by means of the pfnWaitForIdle call, so no special handling is required.
+ *
+ * @msc
+ * VM2, VM1, IntNet, NetFlt, Kernel, Wire;
+ *
+ * --- [label="Rediscovery conditions are not met"];
+ * VM1->IntNet [label="pkt0"];
+ * IntNet=>IntNet [label="Lock Network"];
+ * IntNet=>IntNet [label="Route packet -> wire"];
+ * IntNet=>IntNet [label="Unlock Network"];
+ * IntNet=>NetFlt [label="pkt0 to wire"];
+ * NetFlt=>NetFlt [label="Read pIf(==NULL) w/ spinlock"];
+ * IntNet<<NetFlt [label="pkt0 to wire (dropped)"];
+ *
+ * --- [label="Rediscovery conditions"];
+ * VM1->IntNet [label="pkt1"];
+ * IntNet=>IntNet [label="Lock Network"];
+ * IntNet=>IntNet [label="Route packet -> wire"];
+ * IntNet=>IntNet [label="Unlock Network"];
+ * IntNet=>NetFlt [label="pkt1 to wire"];
+ * NetFlt=>NetFlt [label="Read pIf(==NULL) w/ spinlock"];
+ * NetFlt=>NetFlt [label="fRediscoveryPending=true w/ spinlock"];
+ * NetFlt=>Kernel [label="ifnet_find_by_name"];
+ * NetFlt<<Kernel [label="ifnet_find_by_name (success)"];
+ *
+ * VM2->IntNet [label="pkt2", linecolor="red", textcolor="red"];
+ * IntNet=>IntNet [label="Lock Network", linecolor="red", textcolor="red"];
+ * IntNet=>IntNet [label="Route packet -> wire", linecolor="red", textcolor="red"];
+ * IntNet=>IntNet [label="Unlock Network", linecolor="red", textcolor="red"];
+ * IntNet=>NetFlt [label="pkt2 to wire", linecolor="red", textcolor="red"];
+ * NetFlt=>NetFlt [label="!pIf || fRediscoveryPending (w/ spinlock)", linecolor="red", textcolor="red"];
+ * IntNet<<NetFlt [label="pkt2 to wire (dropped)", linecolor="red", textcolor="red"];
+
+ * NetFlt=>Kernel [label="iflt_attach"];
+ * NetFlt<<Kernel [label="iflt_attach (success)"];
+ * NetFlt=>NetFlt [label="Acquire spinlock"];
+ * NetFlt=>NetFlt [label="Set pIf and update flags"];
+ * NetFlt=>NetFlt [label="Release spinlock"];
+ *
+ * NetFlt=>Kernel [label="pkt1 to wire"];
+ * Kernel->Wire [label="pkt1 to wire"];
+ * NetFlt<<Kernel [label="pkt1 to wire"];
+ * IntNet<<NetFlt [label="pkt1 to wire"];
+ *
+ *
+ * @endmsc
+ *
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_NET_FLT_DRV
+#include "VBoxNetFltInternal.h"
+
+#include <VBox/sup.h>
+#include <VBox/log.h>
+#include <VBox/err.h>
+#include <iprt/assert.h>
+#include <iprt/string.h>
+#include <iprt/spinlock.h>
+#include <iprt/uuid.h>
+#include <iprt/mem.h>
+#include <iprt/time.h>
+#include <iprt/semaphore.h>
+#include <iprt/thread.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#define IFPORT_2_VBOXNETFLTINS(pIfPort) \
+ ( (PVBOXNETFLTINS)((uint8_t *)(pIfPort) - RT_UOFFSETOF(VBOXNETFLTINS, MyPort)) )
+
+
+AssertCompileMemberSize(VBOXNETFLTINS, enmState, sizeof(uint32_t));
+
+/**
+ * Sets the enmState member atomically.
+ *
+ * Used for all updates.
+ *
+ * @param pThis The instance.
+ * @param enmNewState The new value.
+ */
+DECLINLINE(void) vboxNetFltSetState(PVBOXNETFLTINS pThis, VBOXNETFTLINSSTATE enmNewState)
+{
+ ASMAtomicWriteU32((uint32_t volatile *)&pThis->enmState, enmNewState);
+}
+
+
+/**
+ * Gets the enmState member atomically.
+ *
+ * Used for all reads.
+ *
+ * @returns The enmState value.
+ * @param pThis The instance.
+ */
+DECLINLINE(VBOXNETFTLINSSTATE) vboxNetFltGetState(PVBOXNETFLTINS pThis)
+{
+ return (VBOXNETFTLINSSTATE)ASMAtomicUoReadU32((uint32_t volatile *)&pThis->enmState);
+}
+
+
+/**
+ * Finds a instance by its name, the caller does the locking.
+ *
+ * @returns Pointer to the instance by the given name. NULL if not found.
+ * @param pGlobals The globals.
+ * @param pszName The name of the instance.
+ */
+static PVBOXNETFLTINS vboxNetFltFindInstanceLocked(PVBOXNETFLTGLOBALS pGlobals, const char *pszName)
+{
+ PVBOXNETFLTINS pCur;
+ for (pCur = pGlobals->pInstanceHead; pCur; pCur = pCur->pNext)
+ if (!strcmp(pszName, pCur->szName))
+ return pCur;
+ return NULL;
+}
+
+
+/**
+ * Finds a instance by its name, will request the mutex.
+ *
+ * No reference to the instance is retained, we're assuming the caller to
+ * already have one but just for some reason doesn't have the pointer to it.
+ *
+ * @returns Pointer to the instance by the given name. NULL if not found.
+ * @param pGlobals The globals.
+ * @param pszName The name of the instance.
+ */
+DECLHIDDEN(PVBOXNETFLTINS) vboxNetFltFindInstance(PVBOXNETFLTGLOBALS pGlobals, const char *pszName)
+{
+ PVBOXNETFLTINS pRet;
+ int rc = RTSemFastMutexRequest(pGlobals->hFastMtx);
+ AssertRCReturn(rc, NULL);
+
+ pRet = vboxNetFltFindInstanceLocked(pGlobals, pszName);
+
+ rc = RTSemFastMutexRelease(pGlobals->hFastMtx);
+ AssertRC(rc);
+ return pRet;
+}
+
+
+/**
+ * Unlinks an instance from the chain.
+ *
+ * @param pGlobals The globals.
+ * @param pToUnlink The instance to unlink.
+ */
+static void vboxNetFltUnlinkLocked(PVBOXNETFLTGLOBALS pGlobals, PVBOXNETFLTINS pToUnlink)
+{
+ if (pGlobals->pInstanceHead == pToUnlink)
+ pGlobals->pInstanceHead = pToUnlink->pNext;
+ else
+ {
+ PVBOXNETFLTINS pCur;
+ for (pCur = pGlobals->pInstanceHead; pCur; pCur = pCur->pNext)
+ if (pCur->pNext == pToUnlink)
+ {
+ pCur->pNext = pToUnlink->pNext;
+ break;
+ }
+ Assert(pCur);
+ }
+ pToUnlink->pNext = NULL;
+}
+
+
+/**
+ * Performs interface rediscovery if it was disconnected from the host.
+ *
+ * @returns true if successfully rediscovered and connected, false if not.
+ * @param pThis The instance.
+ */
+static bool vboxNetFltMaybeRediscovered(PVBOXNETFLTINS pThis)
+{
+ uint64_t Now;
+ bool fRediscovered;
+ bool fDoIt;
+
+ /*
+ * Don't do rediscovery if we're called with preemption disabled.
+ *
+ * Note! This may cause trouble if we're always called with preemption
+ * disabled and vboxNetFltOsMaybeRediscovered actually does some real
+ * work. For the time being though, only Darwin and FreeBSD depends
+ * on these call outs and neither supports sending with preemption
+ * disabled.
+ */
+ if (!RTThreadPreemptIsEnabled(NIL_RTTHREAD))
+ return false;
+
+ /*
+ * Rediscovered already? Time to try again?
+ */
+ Now = RTTimeNanoTS();
+ RTSpinlockAcquire(pThis->hSpinlock);
+
+ fRediscovered = !ASMAtomicUoReadBool(&pThis->fDisconnectedFromHost);
+ fDoIt = !fRediscovered
+ && !ASMAtomicUoReadBool(&pThis->fRediscoveryPending)
+ && Now - ASMAtomicUoReadU64(&pThis->NanoTSLastRediscovery) > UINT64_C(5000000000); /* 5 sec */
+ if (fDoIt)
+ ASMAtomicWriteBool(&pThis->fRediscoveryPending, true);
+
+ RTSpinlockRelease(pThis->hSpinlock);
+
+ /*
+ * Call the OS specific code to do the job.
+ * Update the state when the call returns, that is everything except for
+ * the fDisconnectedFromHost flag which the OS specific code shall set.
+ */
+ if (fDoIt)
+ {
+ fRediscovered = vboxNetFltOsMaybeRediscovered(pThis);
+
+ Assert(!fRediscovered || !ASMAtomicUoReadBool(&pThis->fDisconnectedFromHost));
+
+ ASMAtomicUoWriteU64(&pThis->NanoTSLastRediscovery, RTTimeNanoTS());
+ ASMAtomicWriteBool(&pThis->fRediscoveryPending, false);
+
+ if (fRediscovered)
+ /** @todo this isn't 100% serialized. */
+ vboxNetFltPortOsSetActive(pThis, pThis->enmTrunkState == INTNETTRUNKIFSTATE_ACTIVE);
+ }
+
+ return fRediscovered;
+}
+
+
+/**
+ * @copydoc INTNETTRUNKIFPORT::pfnXmit
+ */
+static DECLCALLBACK(int) vboxNetFltPortXmit(PINTNETTRUNKIFPORT pIfPort, void *pvIfData, PINTNETSG pSG, uint32_t fDst)
+{
+ PVBOXNETFLTINS pThis = IFPORT_2_VBOXNETFLTINS(pIfPort);
+ int rc = VINF_SUCCESS;
+
+ /*
+ * Input validation.
+ */
+ AssertPtr(pThis);
+ AssertPtr(pSG);
+ Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION);
+ AssertReturn(vboxNetFltGetState(pThis) == kVBoxNetFltInsState_Connected, VERR_INVALID_STATE);
+
+ /*
+ * Do a busy retain and then make sure we're connected to the interface
+ * before invoking the OS specific code.
+ */
+ if (RT_LIKELY(vboxNetFltTryRetainBusyActive(pThis)))
+ {
+ if ( !ASMAtomicUoReadBool(&pThis->fDisconnectedFromHost)
+ || vboxNetFltMaybeRediscovered(pThis))
+ rc = vboxNetFltPortOsXmit(pThis, pvIfData, pSG, fDst);
+ vboxNetFltRelease(pThis, true /* fBusy */);
+ }
+
+ return rc;
+}
+
+
+/**
+ * @copydoc INTNETTRUNKIFPORT::pfnWaitForIdle
+ */
+static DECLCALLBACK(int) vboxNetFltPortWaitForIdle(PINTNETTRUNKIFPORT pIfPort, uint32_t cMillies)
+{
+ PVBOXNETFLTINS pThis = IFPORT_2_VBOXNETFLTINS(pIfPort);
+ int rc;
+
+ /*
+ * Input validation.
+ */
+ AssertPtr(pThis);
+ Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION);
+ AssertReturn(vboxNetFltGetState(pThis) == kVBoxNetFltInsState_Connected, VERR_INVALID_STATE);
+ AssertReturn(pThis->enmTrunkState == INTNETTRUNKIFSTATE_DISCONNECTING, VERR_INVALID_STATE);
+
+ /*
+ * Go to sleep on the semaphore after checking the busy count.
+ */
+ vboxNetFltRetain(pThis, false /* fBusy */);
+
+ rc = VINF_SUCCESS;
+ while (pThis->cBusy && RT_SUCCESS(rc))
+ rc = RTSemEventWait(pThis->hEventIdle, cMillies); /** @todo make interruptible? */
+
+ vboxNetFltRelease(pThis, false /* fBusy */);
+
+ return rc;
+}
+
+
+/**
+ * @copydoc INTNETTRUNKIFPORT::pfnSetState
+ */
+static DECLCALLBACK(INTNETTRUNKIFSTATE) vboxNetFltPortSetState(PINTNETTRUNKIFPORT pIfPort, INTNETTRUNKIFSTATE enmState)
+{
+ PVBOXNETFLTINS pThis = IFPORT_2_VBOXNETFLTINS(pIfPort);
+ INTNETTRUNKIFSTATE enmOldTrunkState;
+
+ /*
+ * Input validation.
+ */
+ AssertPtr(pThis);
+ AssertPtr(pThis->pGlobals);
+ Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION);
+ AssertReturn(vboxNetFltGetState(pThis) == kVBoxNetFltInsState_Connected, INTNETTRUNKIFSTATE_INVALID);
+ AssertReturn(enmState > INTNETTRUNKIFSTATE_INVALID && enmState < INTNETTRUNKIFSTATE_END,
+ INTNETTRUNKIFSTATE_INVALID);
+
+ /*
+ * Take the lock and change the state.
+ */
+ RTSpinlockAcquire(pThis->hSpinlock);
+ enmOldTrunkState = pThis->enmTrunkState;
+ if (enmOldTrunkState != enmState)
+ ASMAtomicWriteU32((uint32_t volatile *)&pThis->enmTrunkState, enmState);
+ RTSpinlockRelease(pThis->hSpinlock);
+
+ /*
+ * If the state change indicates that the trunk has become active or
+ * inactive, call the OS specific part so they can work the promiscuous
+ * settings and such.
+ * Note! The caller makes sure there are no concurrent pfnSetState calls.
+ */
+ if ((enmOldTrunkState == INTNETTRUNKIFSTATE_ACTIVE) != (enmState == INTNETTRUNKIFSTATE_ACTIVE))
+ vboxNetFltPortOsSetActive(pThis, (enmState == INTNETTRUNKIFSTATE_ACTIVE));
+
+ return enmOldTrunkState;
+}
+
+
+/**
+ * @copydoc INTNETTRUNKIFPORT::pfnNotifyMacAddress
+ */
+static DECLCALLBACK(void) vboxNetFltPortNotifyMacAddress(PINTNETTRUNKIFPORT pIfPort, void *pvIfData, PCRTMAC pMac)
+{
+ PVBOXNETFLTINS pThis = IFPORT_2_VBOXNETFLTINS(pIfPort);
+
+ /*
+ * Input validation.
+ */
+ AssertPtr(pThis);
+ Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION);
+
+ vboxNetFltRetain(pThis, false /* fBusy */);
+ vboxNetFltPortOsNotifyMacAddress(pThis, pvIfData, pMac);
+ vboxNetFltRelease(pThis, false /* fBusy */);
+}
+
+
+/**
+ * @copydoc INTNETTRUNKIFPORT::pfnConnectInterface
+ */
+static DECLCALLBACK(int) vboxNetFltPortConnectInterface(PINTNETTRUNKIFPORT pIfPort, void *pvIf, void **ppvIfData)
+{
+ PVBOXNETFLTINS pThis = IFPORT_2_VBOXNETFLTINS(pIfPort);
+ int rc;
+
+ /*
+ * Input validation.
+ */
+ AssertPtr(pThis);
+ Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION);
+
+ vboxNetFltRetain(pThis, false /* fBusy */);
+ rc = vboxNetFltPortOsConnectInterface(pThis, pvIf, ppvIfData);
+ vboxNetFltRelease(pThis, false /* fBusy */);
+
+ return rc;
+}
+
+
+/**
+ * @copydoc INTNETTRUNKIFPORT::pfnDisconnectInterface
+ */
+static DECLCALLBACK(void) vboxNetFltPortDisconnectInterface(PINTNETTRUNKIFPORT pIfPort, void *pvIfData)
+{
+ PVBOXNETFLTINS pThis = IFPORT_2_VBOXNETFLTINS(pIfPort);
+ int rc;
+
+ /*
+ * Input validation.
+ */
+ AssertPtr(pThis);
+ Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION);
+
+ vboxNetFltRetain(pThis, false /* fBusy */);
+ rc = vboxNetFltPortOsDisconnectInterface(pThis, pvIfData);
+ vboxNetFltRelease(pThis, false /* fBusy */);
+ AssertRC(rc); /** @todo fix vboxNetFltPortOsDisconnectInterface. */
+}
+
+
+/**
+ * @copydoc INTNETTRUNKIFPORT::pfnDisconnectAndRelease
+ */
+static DECLCALLBACK(void) vboxNetFltPortDisconnectAndRelease(PINTNETTRUNKIFPORT pIfPort)
+{
+ PVBOXNETFLTINS pThis = IFPORT_2_VBOXNETFLTINS(pIfPort);
+
+ /*
+ * Serious paranoia.
+ */
+ AssertPtr(pThis);
+ Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION);
+ Assert(pThis->MyPort.u32VersionEnd == INTNETTRUNKIFPORT_VERSION);
+ AssertPtr(pThis->pGlobals);
+ Assert(pThis->hEventIdle != NIL_RTSEMEVENT);
+ Assert(pThis->hSpinlock != NIL_RTSPINLOCK);
+ Assert(pThis->szName[0]);
+
+ Assert(vboxNetFltGetState(pThis) == kVBoxNetFltInsState_Connected);
+ Assert(pThis->enmTrunkState == INTNETTRUNKIFSTATE_DISCONNECTING);
+ Assert(!pThis->fRediscoveryPending);
+ Assert(!pThis->cBusy);
+
+ /*
+ * Disconnect and release it.
+ */
+ RTSpinlockAcquire(pThis->hSpinlock);
+ vboxNetFltSetState(pThis, kVBoxNetFltInsState_Disconnecting);
+ RTSpinlockRelease(pThis->hSpinlock);
+
+ vboxNetFltOsDisconnectIt(pThis);
+ pThis->pSwitchPort = NULL;
+
+#ifdef VBOXNETFLT_STATIC_CONFIG
+ RTSpinlockAcquire(pThis->hSpinlock);
+ vboxNetFltSetState(pThis, kVBoxNetFltInsState_Unconnected);
+ RTSpinlockRelease(pThis->hSpinlock);
+#endif
+
+ vboxNetFltRelease(pThis, false /* fBusy */);
+}
+
+
+/**
+ * Destroy a device that has been disconnected from the switch.
+ *
+ * @returns true if the instance is destroyed, false otherwise.
+ * @param pThis The instance to be destroyed. This is
+ * no longer valid when this function returns.
+ */
+static bool vboxNetFltDestroyInstance(PVBOXNETFLTINS pThis)
+{
+ PVBOXNETFLTGLOBALS pGlobals = pThis->pGlobals;
+ uint32_t cRefs = ASMAtomicUoReadU32((uint32_t volatile *)&pThis->cRefs);
+ int rc;
+ LogFlow(("vboxNetFltDestroyInstance: pThis=%p (%s)\n", pThis, pThis->szName));
+
+ /*
+ * Validate the state.
+ */
+#ifdef VBOXNETFLT_STATIC_CONFIG
+ Assert( vboxNetFltGetState(pThis) == kVBoxNetFltInsState_Disconnecting
+ || vboxNetFltGetState(pThis) == kVBoxNetFltInsState_Unconnected);
+#else
+ Assert(vboxNetFltGetState(pThis) == kVBoxNetFltInsState_Disconnecting);
+#endif
+ Assert(pThis->enmTrunkState == INTNETTRUNKIFSTATE_DISCONNECTING);
+ Assert(!pThis->fRediscoveryPending);
+ Assert(!pThis->cRefs);
+ Assert(!pThis->cBusy);
+ Assert(!pThis->pSwitchPort);
+
+ /*
+ * Make sure the state is 'disconnecting' / 'destroying' and let the OS
+ * specific code do its part of the cleanup outside the mutex.
+ */
+ rc = RTSemFastMutexRequest(pGlobals->hFastMtx); AssertRC(rc);
+ vboxNetFltSetState(pThis, kVBoxNetFltInsState_Disconnecting);
+ RTSemFastMutexRelease(pGlobals->hFastMtx);
+
+ vboxNetFltOsDeleteInstance(pThis);
+
+ /*
+ * Unlink the instance and free up its resources.
+ */
+ rc = RTSemFastMutexRequest(pGlobals->hFastMtx); AssertRC(rc);
+ vboxNetFltSetState(pThis, kVBoxNetFltInsState_Destroyed);
+ vboxNetFltUnlinkLocked(pGlobals, pThis);
+ RTSemFastMutexRelease(pGlobals->hFastMtx);
+
+ RTSemEventDestroy(pThis->hEventIdle);
+ pThis->hEventIdle = NIL_RTSEMEVENT;
+ RTSpinlockDestroy(pThis->hSpinlock);
+ pThis->hSpinlock = NIL_RTSPINLOCK;
+ RTMemFree(pThis);
+
+ NOREF(cRefs);
+
+ return true;
+}
+
+
+/**
+ * Releases a reference to the specified instance.
+ *
+ * This method will destroy the instance when the count reaches 0.
+ * It will also take care of decrementing the counter and idle wakeup.
+ *
+ * @param pThis The instance.
+ * @param fBusy Whether the busy counter should be decremented too.
+ */
+DECLHIDDEN(void) vboxNetFltRelease(PVBOXNETFLTINS pThis, bool fBusy)
+{
+ uint32_t cRefs;
+
+ /*
+ * Paranoid Android.
+ */
+ AssertPtr(pThis);
+ Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION);
+ Assert(pThis->MyPort.u32VersionEnd == INTNETTRUNKIFPORT_VERSION);
+ Assert( vboxNetFltGetState(pThis) > kVBoxNetFltInsState_Invalid
+ && vboxNetFltGetState(pThis) < kVBoxNetFltInsState_Destroyed);
+ AssertPtr(pThis->pGlobals);
+ Assert(pThis->hEventIdle != NIL_RTSEMEVENT);
+ Assert(pThis->hSpinlock != NIL_RTSPINLOCK);
+ Assert(pThis->szName[0]);
+
+ /*
+ * Work the busy counter.
+ */
+ if (fBusy)
+ {
+ cRefs = ASMAtomicDecU32(&pThis->cBusy);
+ if (!cRefs)
+ {
+ int rc = RTSemEventSignal(pThis->hEventIdle);
+ AssertRC(rc);
+ }
+ else
+ Assert(cRefs < UINT32_MAX / 2);
+ }
+
+ /*
+ * The object reference counting.
+ */
+ cRefs = ASMAtomicDecU32(&pThis->cRefs);
+ if (!cRefs)
+ vboxNetFltDestroyInstance(pThis);
+ else
+ Assert(cRefs < UINT32_MAX / 2);
+}
+
+
+/**
+ * @copydoc INTNETTRUNKIFPORT::pfnRelease
+ */
+static DECLCALLBACK(void) vboxNetFltPortRelease(PINTNETTRUNKIFPORT pIfPort)
+{
+ PVBOXNETFLTINS pThis = IFPORT_2_VBOXNETFLTINS(pIfPort);
+ vboxNetFltRelease(pThis, false /* fBusy */);
+}
+
+
+/**
+ * @callback_method_impl{FNINTNETTRUNKIFPORTRELEASEBUSY}
+ */
+DECL_HIDDEN_CALLBACK(void) vboxNetFltPortReleaseBusy(PINTNETTRUNKIFPORT pIfPort)
+{
+ PVBOXNETFLTINS pThis = IFPORT_2_VBOXNETFLTINS(pIfPort);
+ vboxNetFltRelease(pThis, true /*fBusy*/);
+}
+
+
+/**
+ * Retains a reference to the specified instance and a busy reference too.
+ *
+ * @param pThis The instance.
+ * @param fBusy Whether the busy counter should be incremented as well.
+ */
+DECLHIDDEN(void) vboxNetFltRetain(PVBOXNETFLTINS pThis, bool fBusy)
+{
+ uint32_t cRefs;
+
+ /*
+ * Paranoid Android.
+ */
+ AssertPtr(pThis);
+ Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION);
+ Assert(pThis->MyPort.u32VersionEnd == INTNETTRUNKIFPORT_VERSION);
+ Assert( vboxNetFltGetState(pThis) > kVBoxNetFltInsState_Invalid
+ && vboxNetFltGetState(pThis) < kVBoxNetFltInsState_Destroyed);
+ AssertPtr(pThis->pGlobals);
+ Assert(pThis->hEventIdle != NIL_RTSEMEVENT);
+ Assert(pThis->hSpinlock != NIL_RTSPINLOCK);
+ Assert(pThis->szName[0]);
+
+ /*
+ * Retain the object.
+ */
+ cRefs = ASMAtomicIncU32(&pThis->cRefs);
+ Assert(cRefs > 1 && cRefs < UINT32_MAX / 2);
+
+ /*
+ * Work the busy counter.
+ */
+ if (fBusy)
+ {
+ cRefs = ASMAtomicIncU32(&pThis->cBusy);
+ Assert(cRefs > 0 && cRefs < UINT32_MAX / 2);
+ }
+
+ NOREF(cRefs);
+}
+
+
+/**
+ * Tries to retain the device as busy if the trunk is active.
+ *
+ * This is used before calling pfnRecv or pfnPreRecv.
+ *
+ * @returns true if we succeeded in retaining a busy reference to the active
+ * device. false if we failed.
+ * @param pThis The instance.
+ */
+DECLHIDDEN(bool) vboxNetFltTryRetainBusyActive(PVBOXNETFLTINS pThis)
+{
+ uint32_t cRefs;
+ bool fRc;
+
+ /*
+ * Paranoid Android.
+ */
+ AssertPtr(pThis);
+ Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION);
+ Assert(pThis->MyPort.u32VersionEnd == INTNETTRUNKIFPORT_VERSION);
+ Assert( vboxNetFltGetState(pThis) > kVBoxNetFltInsState_Invalid
+ && vboxNetFltGetState(pThis) < kVBoxNetFltInsState_Destroyed);
+ AssertPtr(pThis->pGlobals);
+ Assert(pThis->hEventIdle != NIL_RTSEMEVENT);
+ Assert(pThis->hSpinlock != NIL_RTSPINLOCK);
+ Assert(pThis->szName[0]);
+
+ /*
+ * Do the retaining and checking behind the spinlock.
+ */
+ RTSpinlockAcquire(pThis->hSpinlock);
+ fRc = pThis->enmTrunkState == INTNETTRUNKIFSTATE_ACTIVE;
+ if (fRc)
+ {
+ cRefs = ASMAtomicIncU32(&pThis->cRefs);
+ AssertMsg(cRefs > 1 && cRefs < UINT32_MAX / 2, ("%d\n", cRefs)); NOREF(cRefs);
+
+ cRefs = ASMAtomicIncU32(&pThis->cBusy);
+ AssertMsg(cRefs >= 1 && cRefs < UINT32_MAX / 2, ("%d\n", cRefs)); NOREF(cRefs);
+ }
+ RTSpinlockRelease(pThis->hSpinlock);
+
+ return fRc;
+}
+
+
+/**
+ * Tries to retain the device as busy if the trunk is not disconnecting.
+ *
+ * This is used before reporting stuff to the internal network.
+ *
+ * @returns true if we succeeded in retaining a busy reference to the active
+ * device. false if we failed.
+ * @param pThis The instance.
+ */
+DECLHIDDEN(bool) vboxNetFltTryRetainBusyNotDisconnected(PVBOXNETFLTINS pThis)
+{
+ uint32_t cRefs;
+ bool fRc;
+
+ /*
+ * Paranoid Android.
+ */
+ AssertPtr(pThis);
+ Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION);
+ Assert(pThis->MyPort.u32VersionEnd == INTNETTRUNKIFPORT_VERSION);
+ Assert( vboxNetFltGetState(pThis) > kVBoxNetFltInsState_Invalid
+ && vboxNetFltGetState(pThis) < kVBoxNetFltInsState_Destroyed);
+ AssertPtr(pThis->pGlobals);
+ Assert(pThis->hEventIdle != NIL_RTSEMEVENT);
+ Assert(pThis->hSpinlock != NIL_RTSPINLOCK);
+ Assert(pThis->szName[0]);
+
+ /*
+ * Do the retaining and checking behind the spinlock.
+ */
+ RTSpinlockAcquire(pThis->hSpinlock);
+ fRc = pThis->enmTrunkState == INTNETTRUNKIFSTATE_ACTIVE
+ || pThis->enmTrunkState == INTNETTRUNKIFSTATE_INACTIVE;
+ if (fRc)
+ {
+ cRefs = ASMAtomicIncU32(&pThis->cRefs);
+ AssertMsg(cRefs > 1 && cRefs < UINT32_MAX / 2, ("%d\n", cRefs)); NOREF(cRefs);
+
+ cRefs = ASMAtomicIncU32(&pThis->cBusy);
+ AssertMsg(cRefs >= 1 && cRefs < UINT32_MAX / 2, ("%d\n", cRefs)); NOREF(cRefs);
+ }
+ RTSpinlockRelease(pThis->hSpinlock);
+
+ return fRc;
+}
+
+
+/**
+ * @copydoc INTNETTRUNKIFPORT::pfnRetain
+ */
+static DECLCALLBACK(void) vboxNetFltPortRetain(PINTNETTRUNKIFPORT pIfPort)
+{
+ PVBOXNETFLTINS pThis = IFPORT_2_VBOXNETFLTINS(pIfPort);
+ vboxNetFltRetain(pThis, false /* fBusy */);
+}
+
+
+/**
+ * Connects the instance to the specified switch port.
+ *
+ * Called while owning the lock. We're ASSUMING that the internal
+ * networking code is already owning an recursive mutex, so, there
+ * will be no deadlocks when vboxNetFltOsConnectIt calls back into
+ * it for setting preferences.
+ *
+ * @returns VBox status code.
+ * @param pThis The instance.
+ * @param pSwitchPort The port on the internal network 'switch'.
+ * @param ppIfPort Where to return our port interface.
+ */
+static int vboxNetFltConnectIt(PVBOXNETFLTINS pThis, PINTNETTRUNKSWPORT pSwitchPort, PINTNETTRUNKIFPORT *ppIfPort)
+{
+ int rc;
+
+ /*
+ * Validate state.
+ */
+ Assert(!pThis->fRediscoveryPending);
+ Assert(!pThis->cBusy);
+#ifdef VBOXNETFLT_STATIC_CONFIG
+ Assert(vboxNetFltGetState(pThis) == kVBoxNetFltInsState_Unconnected);
+#else
+ Assert(vboxNetFltGetState(pThis) == kVBoxNetFltInsState_Initializing);
+#endif
+ Assert(pThis->enmTrunkState == INTNETTRUNKIFSTATE_INACTIVE);
+
+ /*
+ * Do the job.
+ * Note that we're calling the os stuff while owning the semaphore here.
+ */
+ pThis->pSwitchPort = pSwitchPort;
+ rc = vboxNetFltOsConnectIt(pThis);
+ if (RT_SUCCESS(rc))
+ {
+ vboxNetFltSetState(pThis, kVBoxNetFltInsState_Connected);
+ *ppIfPort = &pThis->MyPort;
+ }
+ else
+ pThis->pSwitchPort = NULL;
+
+ Assert(pThis->enmTrunkState == INTNETTRUNKIFSTATE_INACTIVE);
+ return rc;
+}
+
+
+/**
+ * Creates a new instance.
+ *
+ * The new instance will be in the suspended state in a dynamic config and in
+ * the inactive in a static one.
+ *
+ * Called without owning the lock, but will request is several times.
+ *
+ * @returns VBox status code.
+ * @param pGlobals The globals.
+ * @param pszName The instance name.
+ * @param pSwitchPort The port on the switch that we're connected with (dynamic only).
+ * @param fNoPromisc Do not attempt going into promiscuous mode.
+ * @param pvContext Context argument for vboxNetFltOsInitInstance.
+ * @param ppIfPort Where to store the pointer to our port interface (dynamic only).
+ */
+static int vboxNetFltNewInstance(PVBOXNETFLTGLOBALS pGlobals, const char *pszName, PINTNETTRUNKSWPORT pSwitchPort,
+ bool fNoPromisc, void *pvContext, PINTNETTRUNKIFPORT *ppIfPort)
+{
+ /*
+ * Allocate and initialize a new instance before requesting the mutex.
+ * Note! That in a static config we'll initialize the trunk state to
+ * disconnecting and flip it in vboxNetFltFactoryCreateAndConnect
+ * later on. This better reflext the state and it works better with
+ * assertions in the destruction path.
+ */
+ int rc;
+ size_t const cchName = strlen(pszName);
+ PVBOXNETFLTINS pNew = (PVBOXNETFLTINS)RTMemAllocZVar(RT_UOFFSETOF_DYN(VBOXNETFLTINS, szName[cchName + 1]));
+ if (!pNew)
+ return VERR_INTNET_FLT_IF_FAILED;
+ AssertMsg(((uintptr_t)pNew & 7) == 0, ("%p LB %#x\n", pNew, RT_UOFFSETOF_DYN(VBOXNETFLTINS, szName[cchName + 1])));
+ pNew->pNext = NULL;
+ pNew->MyPort.u32Version = INTNETTRUNKIFPORT_VERSION;
+ pNew->MyPort.pfnRetain = vboxNetFltPortRetain;
+ pNew->MyPort.pfnRelease = vboxNetFltPortRelease;
+ pNew->MyPort.pfnDisconnectAndRelease= vboxNetFltPortDisconnectAndRelease;
+ pNew->MyPort.pfnSetState = vboxNetFltPortSetState;
+ pNew->MyPort.pfnWaitForIdle = vboxNetFltPortWaitForIdle;
+ pNew->MyPort.pfnXmit = vboxNetFltPortXmit;
+ pNew->MyPort.pfnNotifyMacAddress = vboxNetFltPortNotifyMacAddress;
+ pNew->MyPort.pfnConnectInterface = vboxNetFltPortConnectInterface;
+ pNew->MyPort.pfnDisconnectInterface = vboxNetFltPortDisconnectInterface;
+ pNew->MyPort.u32VersionEnd = INTNETTRUNKIFPORT_VERSION;
+ pNew->pSwitchPort = pSwitchPort;
+ pNew->pGlobals = pGlobals;
+ pNew->hSpinlock = NIL_RTSPINLOCK;
+ pNew->enmState = kVBoxNetFltInsState_Initializing;
+#ifdef VBOXNETFLT_STATIC_CONFIG
+ pNew->enmTrunkState = INTNETTRUNKIFSTATE_DISCONNECTING;
+#else
+ pNew->enmTrunkState = INTNETTRUNKIFSTATE_INACTIVE;
+#endif
+ pNew->fDisconnectedFromHost = false;
+ pNew->fRediscoveryPending = false;
+ pNew->fDisablePromiscuous = fNoPromisc;
+ pNew->NanoTSLastRediscovery = INT64_MAX;
+ pNew->cRefs = 1;
+ pNew->cBusy = 0;
+ pNew->hEventIdle = NIL_RTSEMEVENT;
+ RT_BCOPY_UNFORTIFIED(pNew->szName, pszName, cchName + 1);
+
+ rc = RTSpinlockCreate(&pNew->hSpinlock, RTSPINLOCK_FLAGS_INTERRUPT_SAFE, "VBoxNetFltNewInstance");
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTSemEventCreate(&pNew->hEventIdle);
+ if (RT_SUCCESS(rc))
+ {
+ rc = vboxNetFltOsPreInitInstance(pNew);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Insert the instance into the chain, checking for
+ * duplicates first of course (race).
+ */
+ rc = RTSemFastMutexRequest(pGlobals->hFastMtx);
+ if (RT_SUCCESS(rc))
+ {
+ if (!vboxNetFltFindInstanceLocked(pGlobals, pszName))
+ {
+ pNew->pNext = pGlobals->pInstanceHead;
+ pGlobals->pInstanceHead = pNew;
+ RTSemFastMutexRelease(pGlobals->hFastMtx);
+
+ /*
+ * Call the OS specific initialization code.
+ */
+ rc = vboxNetFltOsInitInstance(pNew, pvContext);
+ RTSemFastMutexRequest(pGlobals->hFastMtx);
+ if (RT_SUCCESS(rc))
+ {
+#ifdef VBOXNETFLT_STATIC_CONFIG
+ /*
+ * Static instances are unconnected at birth.
+ */
+ Assert(!pSwitchPort);
+ pNew->enmState = kVBoxNetFltInsState_Unconnected;
+ RTSemFastMutexRelease(pGlobals->hFastMtx);
+ *ppIfPort = &pNew->MyPort;
+ return rc;
+
+#else /* !VBOXNETFLT_STATIC_CONFIG */
+ /*
+ * Connect it as well, the OS specific bits has to be done outside
+ * the lock as they may call back to into intnet.
+ */
+ rc = vboxNetFltConnectIt(pNew, pSwitchPort, ppIfPort);
+ if (RT_SUCCESS(rc))
+ {
+ RTSemFastMutexRelease(pGlobals->hFastMtx);
+ Assert(*ppIfPort == &pNew->MyPort);
+ return rc;
+ }
+
+ /* Bail out (failed). */
+ vboxNetFltOsDeleteInstance(pNew);
+#endif /* !VBOXNETFLT_STATIC_CONFIG */
+ }
+ vboxNetFltUnlinkLocked(pGlobals, pNew);
+ }
+ else
+ rc = VERR_INTNET_FLT_IF_BUSY;
+ RTSemFastMutexRelease(pGlobals->hFastMtx);
+ }
+ }
+ RTSemEventDestroy(pNew->hEventIdle);
+ }
+ RTSpinlockDestroy(pNew->hSpinlock);
+ }
+
+ RTMemFree(pNew);
+ return rc;
+}
+
+
+#ifdef VBOXNETFLT_STATIC_CONFIG
+/**
+ * Searches for the NetFlt instance by its name and creates the new one if not found.
+ *
+ * @returns VBox status code.
+ * @retval VINF_SUCCESS and *ppInstance if a new instance was created.
+ * @retval VINF_ALREADY_INITIALIZED and *ppInstance if an instance already exists.
+ *
+ * @param pGlobal Pointer to the globals.
+ * @param pszName The instance name.
+ * @param ppInstance Where to return the instance pointer on success.
+ * @param pvContext Context which needs to be passed along to vboxNetFltOsInitInstance.
+ */
+DECLHIDDEN(int) vboxNetFltSearchCreateInstance(PVBOXNETFLTGLOBALS pGlobals, const char *pszName, PVBOXNETFLTINS *ppInstance, void *pvContext)
+{
+ PINTNETTRUNKIFPORT pIfPort;
+ PVBOXNETFLTINS pCur;
+ VBOXNETFTLINSSTATE enmState;
+ int rc;
+
+ *ppInstance = NULL;
+ rc = RTSemFastMutexRequest(pGlobals->hFastMtx);
+ AssertRCReturn(rc, rc);
+
+ /*
+ * Look for an existing instance in the list.
+ *
+ * There might be an existing one in the list if the driver was unbound
+ * while it was connected to an internal network. We're running into
+ * a destruction race that is a bit similar to the one in
+ * vboxNetFltFactoryCreateAndConnect, only the roles are reversed
+ * and we're not in a position to back down. Instead of backing down
+ * we'll delay a bit giving the other thread time to complete the
+ * destructor.
+ */
+ pCur = vboxNetFltFindInstanceLocked(pGlobals, pszName);
+ while (pCur)
+ {
+ uint32_t cRefs = ASMAtomicIncU32(&pCur->cRefs);
+ if (cRefs > 1)
+ {
+ enmState = vboxNetFltGetState(pCur);
+ switch (enmState)
+ {
+ case kVBoxNetFltInsState_Unconnected:
+ case kVBoxNetFltInsState_Connected:
+ case kVBoxNetFltInsState_Disconnecting:
+ if (pCur->fDisconnectedFromHost)
+ {
+ /* Wait for it to exit the transitional disconnecting
+ state. It might otherwise be running the risk of
+ upsetting the OS specific code... */
+ /** @todo This reconnect stuff should be serialized correctly for static
+ * devices. Shouldn't it? In the dynamic case we're using the INTNET
+ * outbound thunk lock, but that doesn't quite cut it here, or does
+ * it? We could either transition to initializing or make a callback
+ * while owning the mutex here... */
+ if (enmState == kVBoxNetFltInsState_Disconnecting)
+ {
+ do
+ {
+ RTSemFastMutexRelease(pGlobals->hFastMtx);
+ RTThreadSleep(2); /* (2ms) */
+ RTSemFastMutexRequest(pGlobals->hFastMtx);
+ enmState = vboxNetFltGetState(pCur);
+ }
+ while (enmState == kVBoxNetFltInsState_Disconnecting);
+ AssertMsg(enmState == kVBoxNetFltInsState_Unconnected, ("%d\n", enmState));
+ Assert(pCur->fDisconnectedFromHost);
+ }
+
+ RTSemFastMutexRelease(pGlobals->hFastMtx);
+ *ppInstance = pCur;
+ return VINF_ALREADY_INITIALIZED;
+ }
+ /* fall thru */
+
+ default:
+ {
+ bool fDfH = pCur->fDisconnectedFromHost;
+ RTSemFastMutexRelease(pGlobals->hFastMtx);
+ vboxNetFltRelease(pCur, false /* fBusy */);
+ LogRel(("VBoxNetFlt: Huh? An instance of '%s' already exists! [pCur=%p cRefs=%d fDfH=%RTbool enmState=%d]\n",
+ pszName, pCur, cRefs - 1, fDfH, enmState));
+ *ppInstance = NULL;
+ return VERR_INTNET_FLT_IF_BUSY;
+ }
+ }
+ }
+
+ /* Zero references, it's being destroyed. Delay a bit so the destructor
+ can finish its work and try again. (vboxNetFltNewInstance will fail
+ with duplicate name if we don't.) */
+# ifdef RT_STRICT
+ Assert(cRefs == 1);
+ enmState = vboxNetFltGetState(pCur);
+ AssertMsg( enmState == kVBoxNetFltInsState_Unconnected
+ || enmState == kVBoxNetFltInsState_Disconnecting
+ || enmState == kVBoxNetFltInsState_Destroyed, ("%d\n", enmState));
+# endif
+ ASMAtomicDecU32(&pCur->cRefs);
+ RTSemFastMutexRelease(pGlobals->hFastMtx);
+ RTThreadSleep(2); /* (2ms) */
+ rc = RTSemFastMutexRequest(pGlobals->hFastMtx);
+ AssertRCReturn(rc, rc);
+
+ /* try again */
+ pCur = vboxNetFltFindInstanceLocked(pGlobals, pszName);
+ }
+
+ RTSemFastMutexRelease(pGlobals->hFastMtx);
+
+ /*
+ * Try create a new instance.
+ * (fNoPromisc is overridden in the vboxNetFltFactoryCreateAndConnect path, so pass true here.)
+ */
+ rc = vboxNetFltNewInstance(pGlobals, pszName, NULL, true /* fNoPromisc */, pvContext, &pIfPort);
+ if (RT_SUCCESS(rc))
+ *ppInstance = IFPORT_2_VBOXNETFLTINS(pIfPort);
+ else
+ *ppInstance = NULL;
+
+ return rc;
+}
+#endif /* VBOXNETFLT_STATIC_CONFIG */
+
+
+/**
+ * @copydoc INTNETTRUNKFACTORY::pfnCreateAndConnect
+ */
+static DECLCALLBACK(int) vboxNetFltFactoryCreateAndConnect(PINTNETTRUNKFACTORY pIfFactory, const char *pszName,
+ PINTNETTRUNKSWPORT pSwitchPort, uint32_t fFlags,
+ PINTNETTRUNKIFPORT *ppIfPort)
+{
+ PVBOXNETFLTGLOBALS pGlobals = (PVBOXNETFLTGLOBALS)((uint8_t *)pIfFactory - RT_UOFFSETOF(VBOXNETFLTGLOBALS, TrunkFactory));
+ PVBOXNETFLTINS pCur;
+ int rc;
+
+ LogFlow(("vboxNetFltFactoryCreateAndConnect: 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);
+
+ /*
+ * Static: Find instance, check if busy, connect if not.
+ * Dynamic: Check for duplicate / busy interface instance.
+ */
+ rc = RTSemFastMutexRequest(pGlobals->hFastMtx);
+ AssertRCReturn(rc, rc);
+
+//#if defined(VBOXNETADP) && defined(RT_OS_WINDOWS)
+// /* temporary hack to pick up the first adapter */
+// pCur = pGlobals->pInstanceHead; /** @todo Don't for get to remove this temporary hack... :-) */
+//#else
+ pCur = vboxNetFltFindInstanceLocked(pGlobals, pszName);
+//#endif
+ if (pCur)
+ {
+#ifdef VBOXNETFLT_STATIC_CONFIG
+ /* Try grab a reference. If the count had already reached zero we're racing the
+ destructor code and must back down. */
+ uint32_t cRefs = ASMAtomicIncU32(&pCur->cRefs);
+ if (cRefs > 1)
+ {
+ if (vboxNetFltGetState(pCur) == kVBoxNetFltInsState_Unconnected)
+ {
+ pCur->enmTrunkState = INTNETTRUNKIFSTATE_INACTIVE; /** @todo protect me? */
+ pCur->fDisablePromiscuous = !!(fFlags & INTNETTRUNKFACTORY_FLAG_NO_PROMISC);
+ rc = vboxNetFltConnectIt(pCur, pSwitchPort, ppIfPort);
+ if (RT_SUCCESS(rc))
+ pCur = NULL; /* Don't release it, reference given to the caller. */
+ else
+ pCur->enmTrunkState = INTNETTRUNKIFSTATE_DISCONNECTING;
+ }
+ else
+ rc = VERR_INTNET_FLT_IF_BUSY;
+ }
+ else
+ {
+ Assert(cRefs == 1);
+ ASMAtomicDecU32(&pCur->cRefs);
+ pCur = NULL; /* nothing to release */
+ rc = VERR_INTNET_FLT_IF_NOT_FOUND;
+ }
+
+ RTSemFastMutexRelease(pGlobals->hFastMtx);
+ if (pCur)
+ vboxNetFltRelease(pCur, false /* fBusy */);
+#else
+ rc = VERR_INTNET_FLT_IF_BUSY;
+ RTSemFastMutexRelease(pGlobals->hFastMtx);
+#endif
+ LogFlow(("vboxNetFltFactoryCreateAndConnect: returns %Rrc\n", rc));
+ return rc;
+ }
+
+ RTSemFastMutexRelease(pGlobals->hFastMtx);
+
+#ifdef VBOXNETFLT_STATIC_CONFIG
+ rc = VERR_INTNET_FLT_IF_NOT_FOUND;
+#else
+ /*
+ * Dynamically create a new instance.
+ */
+ rc = vboxNetFltNewInstance(pGlobals,
+ pszName,
+ pSwitchPort,
+ !!(fFlags & INTNETTRUNKFACTORY_FLAG_NO_PROMISC),
+ NULL,
+ ppIfPort);
+#endif
+ LogFlow(("vboxNetFltFactoryCreateAndConnect: returns %Rrc\n", rc));
+ return rc;
+}
+
+
+/**
+ * @copydoc INTNETTRUNKFACTORY::pfnRelease
+ */
+static DECLCALLBACK(void) vboxNetFltFactoryRelease(PINTNETTRUNKFACTORY pIfFactory)
+{
+ PVBOXNETFLTGLOBALS pGlobals = (PVBOXNETFLTGLOBALS)((uint8_t *)pIfFactory - RT_UOFFSETOF(VBOXNETFLTGLOBALS, TrunkFactory));
+
+ int32_t cRefs = ASMAtomicDecS32(&pGlobals->cFactoryRefs);
+ Assert(cRefs >= 0); NOREF(cRefs);
+ LogFlow(("vboxNetFltFactoryRelease: cRefs=%d (new)\n", cRefs));
+}
+
+
+/**
+ * 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 *) vboxNetFltQueryFactoryInterface(PCSUPDRVFACTORY pSupDrvFactory, PSUPDRVSESSION pSession,
+ const char *pszInterfaceUuid)
+{
+ PVBOXNETFLTGLOBALS pGlobals = (PVBOXNETFLTGLOBALS)((uint8_t *)pSupDrvFactory - RT_UOFFSETOF(VBOXNETFLTGLOBALS, 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))
+ {
+ ASMAtomicIncS32(&pGlobals->cFactoryRefs);
+ return &pGlobals->TrunkFactory;
+ }
+#ifdef LOG_ENABLED
+ /* log legacy queries */
+ /* else if (!RTUuidCompareStr(&UuidReq, INTNETTRUNKFACTORY_V1_UUID_STR))
+ Log(("VBoxNetFlt: V1 factory query\n"));
+ */
+ 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;
+}
+
+
+/**
+ * Checks whether the VBoxNetFlt wossname can be unloaded.
+ *
+ * This will return false if someone is currently using the module.
+ *
+ * @returns true if it's relatively safe to unload it, otherwise false.
+ * @param pGlobals Pointer to the globals.
+ */
+DECLHIDDEN(bool) vboxNetFltCanUnload(PVBOXNETFLTGLOBALS pGlobals)
+{
+ int rc = RTSemFastMutexRequest(pGlobals->hFastMtx);
+ bool fRc = !pGlobals->pInstanceHead
+ && pGlobals->cFactoryRefs <= 0;
+ RTSemFastMutexRelease(pGlobals->hFastMtx);
+ AssertRC(rc);
+ return fRc;
+}
+
+
+/**
+ * Try to close the IDC connection to SUPDRV if established.
+ *
+ * @returns VBox status code.
+ * @retval VINF_SUCCESS on success.
+ * @retval VERR_WRONG_ORDER if we're busy.
+ *
+ * @param pGlobals Pointer to the globals.
+ *
+ * @sa vboxNetFltTryDeleteIdcAndGlobals()
+ */
+DECLHIDDEN(int) vboxNetFltTryDeleteIdc(PVBOXNETFLTGLOBALS pGlobals)
+{
+ int rc;
+
+ Assert(pGlobals->hFastMtx != NIL_RTSEMFASTMUTEX);
+
+ /*
+ * Check before trying to deregister the factory.
+ */
+ if (!vboxNetFltCanUnload(pGlobals))
+ return VERR_WRONG_ORDER;
+
+ if (!pGlobals->fIDCOpen)
+ rc = VINF_SUCCESS;
+ else
+ {
+ /*
+ * Disconnect from SUPDRV and check that nobody raced us,
+ * reconnect if that should happen.
+ */
+ rc = SUPR0IdcComponentDeregisterFactory(&pGlobals->SupDrvIDC, &pGlobals->SupDrvFactory);
+ AssertRC(rc);
+ if (!vboxNetFltCanUnload(pGlobals))
+ {
+ rc = SUPR0IdcComponentRegisterFactory(&pGlobals->SupDrvIDC, &pGlobals->SupDrvFactory);
+ AssertRC(rc);
+ return VERR_WRONG_ORDER;
+ }
+
+ SUPR0IdcClose(&pGlobals->SupDrvIDC);
+ pGlobals->fIDCOpen = false;
+ }
+
+ return rc;
+}
+
+
+/**
+ * Establishes the IDC connection to SUPDRV and registers our component factory.
+ *
+ * @returns VBox status code.
+ * @param pGlobals Pointer to the globals.
+ * @sa vboxNetFltInitGlobalsAndIdc().
+ */
+DECLHIDDEN(int) vboxNetFltInitIdc(PVBOXNETFLTGLOBALS pGlobals)
+{
+ int rc;
+ Assert(!pGlobals->fIDCOpen);
+
+ /*
+ * 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))
+ {
+ pGlobals->fIDCOpen = true;
+ Log(("VBoxNetFlt: pSession=%p\n", SUPR0IdcGetSession(&pGlobals->SupDrvIDC)));
+ return rc;
+ }
+
+ /* bail out. */
+ LogRel(("VBoxNetFlt: Failed to register component factory, rc=%Rrc\n", rc));
+ SUPR0IdcClose(&pGlobals->SupDrvIDC);
+ }
+
+ return rc;
+}
+
+
+/**
+ * Deletes the globals.
+ *
+ * This must be called after the IDC connection has been closed,
+ * see vboxNetFltTryDeleteIdc().
+ *
+ * @param pGlobals Pointer to the globals.
+ * @sa vboxNetFltTryDeleteIdcAndGlobals()
+ */
+DECLHIDDEN(void) vboxNetFltDeleteGlobals(PVBOXNETFLTGLOBALS pGlobals)
+{
+ Assert(!pGlobals->fIDCOpen);
+
+ /*
+ * Release resources.
+ */
+ RTSemFastMutexDestroy(pGlobals->hFastMtx);
+ pGlobals->hFastMtx = NIL_RTSEMFASTMUTEX;
+}
+
+
+/**
+ * Initializes the globals.
+ *
+ * @returns VBox status code.
+ * @param pGlobals Pointer to the globals.
+ * @sa vboxNetFltInitGlobalsAndIdc().
+ */
+DECLHIDDEN(int) vboxNetFltInitGlobals(PVBOXNETFLTGLOBALS pGlobals)
+{
+ /*
+ * Initialize the common portions of the structure.
+ */
+ int rc = RTSemFastMutexCreate(&pGlobals->hFastMtx);
+ if (RT_SUCCESS(rc))
+ {
+ pGlobals->pInstanceHead = NULL;
+
+ pGlobals->TrunkFactory.pfnRelease = vboxNetFltFactoryRelease;
+ pGlobals->TrunkFactory.pfnCreateAndConnect = vboxNetFltFactoryCreateAndConnect;
+#if defined(RT_OS_WINDOWS) && defined(VBOXNETADP)
+ memcpy(pGlobals->SupDrvFactory.szName, "VBoxNetAdp", sizeof("VBoxNetAdp"));
+#else
+ memcpy(pGlobals->SupDrvFactory.szName, "VBoxNetFlt", sizeof("VBoxNetFlt"));
+#endif
+ pGlobals->SupDrvFactory.pfnQueryFactoryInterface = vboxNetFltQueryFactoryInterface;
+ pGlobals->fIDCOpen = false;
+
+ return rc;
+ }
+
+ return rc;
+}
+
+
+/**
+ * Called by the native part when the OS wants the driver to unload.
+ *
+ * @returns VINF_SUCCESS on success, VERR_WRONG_ORDER if we're busy.
+ *
+ * @param pGlobals Pointer to the globals.
+ */
+DECLHIDDEN(int) vboxNetFltTryDeleteIdcAndGlobals(PVBOXNETFLTGLOBALS pGlobals)
+{
+ int rc = vboxNetFltTryDeleteIdc(pGlobals);
+ if (RT_SUCCESS(rc))
+ vboxNetFltDeleteGlobals(pGlobals);
+ return rc;
+}
+
+
+/**
+ * Called by the native driver/kext module initialization routine.
+ *
+ * It will initialize the common parts of the globals, assuming the caller
+ * has already taken care of the OS specific bits, and establish the IDC
+ * connection to SUPDRV.
+ *
+ * @returns VBox status code.
+ * @param pGlobals Pointer to the globals.
+ */
+DECLHIDDEN(int) vboxNetFltInitGlobalsAndIdc(PVBOXNETFLTGLOBALS pGlobals)
+{
+ /*
+ * Initialize the common portions of the structure.
+ */
+ int rc = vboxNetFltInitGlobals(pGlobals);
+ if (RT_SUCCESS(rc))
+ {
+ rc = vboxNetFltInitIdc(pGlobals);
+ if (RT_SUCCESS(rc))
+ return rc;
+
+ /* bail out. */
+ vboxNetFltDeleteGlobals(pGlobals);
+ }
+
+ return rc;
+}
+
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/VBoxNetFlt.rc b/src/VBox/HostDrivers/VBoxNetFlt/VBoxNetFlt.rc
new file mode 100644
index 00000000..0205307a
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/VBoxNetFlt.rc
@@ -0,0 +1,69 @@
+/* $Id: VBoxNetFlt.rc $ */
+/** @file
+ * VBoxNetFlt - 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 <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#include <windows.h>
+#include <VBox/version.h>
+
+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", "VirtualBox NDIS 6.0 Lightweight Filter Driver\0"
+ VALUE "InternalName", "VBoxNetLwf\0"
+ VALUE "OriginalFilename", "VBoxNetLwf.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/VBoxNetFlt/VBoxNetFltInternal.h b/src/VBox/HostDrivers/VBoxNetFlt/VBoxNetFltInternal.h
new file mode 100644
index 00000000..1cff0981
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/VBoxNetFltInternal.h
@@ -0,0 +1,499 @@
+/* $Id: VBoxNetFltInternal.h $ */
+/** @file
+ * VBoxNetFlt - 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 <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_SRC_VBoxNetFlt_VBoxNetFltInternal_h
+#define VBOX_INCLUDED_SRC_VBoxNetFlt_VBoxNetFltInternal_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <VBox/sup.h>
+#include <VBox/intnet.h>
+#include <iprt/semaphore.h>
+#include <iprt/assert.h>
+
+
+RT_C_DECLS_BEGIN
+
+/** Pointer to the globals. */
+typedef struct VBOXNETFLTGLOBALS *PVBOXNETFLTGLOBALS;
+
+
+/**
+ * The state of a filter driver instance.
+ *
+ * The state machine differs a bit between the platforms because of
+ * the way we hook into the stack. On some hosts we can dynamically
+ * attach when required (on CreateInstance) and on others we will
+ * have to connect when the network stack is bound up. These modes
+ * are called static and dynamic config and governed at compile time
+ * by the VBOXNETFLT_STATIC_CONFIG define.
+ *
+ * See sec_netflt_msc for more details on locking and synchronization.
+ */
+typedef enum VBOXNETFTLINSSTATE
+{
+ /** The usual invalid state. */
+ kVBoxNetFltInsState_Invalid = 0,
+ /** Initialization.
+ * We've reserved the interface name but need to attach to the actual
+ * network interface outside the lock to avoid deadlocks.
+ * In the dynamic case this happens during a Create(Instance) call.
+ * In the static case it happens during driver initialization. */
+ kVBoxNetFltInsState_Initializing,
+#ifdef VBOXNETFLT_STATIC_CONFIG
+ /** Unconnected, not hooked up to a switch (static only).
+ * The filter driver instance has been instantiated and hooked up,
+ * waiting to be connected to an internal network. */
+ kVBoxNetFltInsState_Unconnected,
+#endif
+ /** Connected to an internal network. */
+ kVBoxNetFltInsState_Connected,
+ /** Disconnecting from the internal network and possibly the host network interface.
+ * Partly for reasons of deadlock avoidance again. */
+ kVBoxNetFltInsState_Disconnecting,
+ /** The instance has been disconnected from both the host and the internal network. */
+ kVBoxNetFltInsState_Destroyed,
+
+ /** The habitual 32-bit enum hack. */
+ kVBoxNetFltInsState_32BitHack = 0x7fffffff
+} VBOXNETFTLINSSTATE;
+
+
+/**
+ * The per-instance data of the VBox filter driver.
+ *
+ * This is data associated with a network interface / NIC / wossname which
+ * the filter driver has been or may be attached to. When possible it is
+ * attached dynamically, but this may not be possible on all OSes so we have
+ * to be flexible about things.
+ *
+ * A network interface / NIC / wossname can only have one filter driver
+ * instance attached to it. So, attempts at connecting an internal network
+ * to an interface that's already in use (connected to another internal network)
+ * will result in a VERR_SHARING_VIOLATION.
+ *
+ * Only one internal network can connect to a filter driver instance.
+ */
+typedef struct VBOXNETFLTINS
+{
+ /** Pointer to the next interface in the list. (VBOXNETFLTGLOBAL::pInstanceHead) */
+ struct VBOXNETFLTINS *pNext;
+ /** 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 the globals. */
+ PVBOXNETFLTGLOBALS pGlobals;
+
+ /** The spinlock protecting the state variables and host interface handle. */
+ RTSPINLOCK hSpinlock;
+ /** The current interface state. */
+ VBOXNETFTLINSSTATE volatile enmState;
+ /** The trunk state. */
+ INTNETTRUNKIFSTATE volatile enmTrunkState;
+ bool volatile fActive;
+ /** Disconnected from the host network interface. */
+ bool volatile fDisconnectedFromHost;
+ /** Rediscovery is pending.
+ * cBusy will never reach zero during rediscovery, so which
+ * takes care of serializing rediscovery and disconnecting. */
+ bool volatile fRediscoveryPending;
+ /** Whether we should not attempt to set promiscuous mode at all. */
+ bool fDisablePromiscuous;
+#if (ARCH_BITS == 32) && defined(__GNUC__)
+#if 0
+ uint32_t u32Padding; /**< Alignment padding, will assert in ASMAtomicUoWriteU64 otherwise. */
+#endif
+#endif
+ /** The timestamp of the last rediscovery. */
+ uint64_t volatile NanoTSLastRediscovery;
+ /** Reference count. */
+ uint32_t volatile cRefs;
+ /** The busy count.
+ * This counts the number of current callers and pending packet. */
+ uint32_t volatile cBusy;
+ /** The event that is signaled when we go idle and that pfnWaitForIdle blocks on. */
+ RTSEMEVENT hEventIdle;
+
+ /** @todo move MacAddr out of this structure! */
+ union
+ {
+#ifdef VBOXNETFLT_OS_SPECFIC
+ struct
+ {
+# if defined(RT_OS_DARWIN)
+ /** @name Darwin instance data.
+ * @{ */
+ /** Pointer to the darwin network interface we're attached to.
+ * This is treated as highly volatile and should only be read and retained
+ * while owning hSpinlock. Releasing references to this should not be done
+ * while owning it though as we might end up destroying it in some paths. */
+ ifnet_t volatile pIfNet;
+ /** The interface filter handle.
+ * Same access rules as with pIfNet. */
+ interface_filter_t volatile pIfFilter;
+ /** Whether we've need to set promiscuous mode when the interface comes up. */
+ bool volatile fNeedSetPromiscuous;
+ /** Whether we've successfully put the interface into to promiscuous mode.
+ * This is for dealing with the ENETDOWN case. */
+ bool volatile fSetPromiscuous;
+ /** The MAC address of the interface. */
+ RTMAC MacAddr;
+ /** PF_SYSTEM socket to listen for events (XXX: globals?) */
+ socket_t pSysSock;
+ /** @} */
+# elif defined(RT_OS_LINUX)
+ /** @name Linux instance data
+ * @{ */
+ /** Pointer to the device. */
+ struct net_device * volatile pDev;
+ /** MTU of host's interface. */
+ uint32_t cbMtu;
+ /** Whether we've successfully put the interface into to promiscuous mode.
+ * This is for dealing with the ENETDOWN case. */
+ bool volatile fPromiscuousSet;
+ /** Whether device exists and physically attached. */
+ bool volatile fRegistered;
+ /** Whether our packet handler is installed. */
+ bool volatile fPacketHandler;
+ /** The MAC address of the interface. */
+ RTMAC MacAddr;
+ struct notifier_block Notifier; /* netdevice */
+ struct notifier_block NotifierIPv4;
+ struct notifier_block NotifierIPv6;
+ struct packet_type PacketType;
+# ifndef VBOXNETFLT_LINUX_NO_XMIT_QUEUE
+ struct sk_buff_head XmitQueue;
+ struct work_struct XmitTask;
+# endif
+ /** @} */
+# elif defined(RT_OS_SOLARIS)
+ /** @name Solaris instance data.
+ * @{ */
+# ifdef VBOX_WITH_NETFLT_CROSSBOW
+ /** Whether the underlying interface is a VNIC or not. */
+ bool fIsVNIC;
+ /** Whether the underlying interface is a VNIC template or not. */
+ bool fIsVNICTemplate;
+ /** Handle to list of created VNICs. */
+ list_t hVNICs;
+ /** The MAC address of the host interface. */
+ RTMAC MacAddr;
+ /** Handle of this interface (lower MAC). */
+ mac_handle_t hInterface;
+ /** Handle to link state notifier. */
+ mac_notify_handle_t hNotify;
+# else
+ /** Pointer to the bound IPv4 stream. */
+ struct vboxnetflt_stream_t * volatile pIp4Stream;
+ /** Pointer to the bound IPv6 stream. */
+ struct vboxnetflt_stream_t * volatile pIp6Stream;
+ /** Pointer to the bound ARP stream. */
+ struct vboxnetflt_stream_t * volatile pArpStream;
+ /** Pointer to the unbound promiscuous stream. */
+ struct vboxnetflt_promisc_stream_t * volatile pPromiscStream;
+ /** Whether we are attaching to IPv6 stream dynamically now. */
+ bool volatile fAttaching;
+ /** Whether this is a VLAN interface or not. */
+ bool volatile fVLAN;
+ /** Layered device handle to the interface. */
+ ldi_handle_t hIface;
+ /** The MAC address of the interface. */
+ RTMAC MacAddr;
+ /** Mutex protection used for loopback. */
+ kmutex_t hMtx;
+ /** Mutex protection used for dynamic IPv6 attaches. */
+ RTSEMFASTMUTEX hPollMtx;
+# endif
+ /** @} */
+# elif defined(RT_OS_FREEBSD)
+ /** @name FreeBSD instance data.
+ * @{ */
+ /** Interface handle */
+ struct ifnet *ifp;
+ /** Netgraph node handle */
+ node_p node;
+ /** Input hook */
+ hook_p input;
+ /** Output hook */
+ hook_p output;
+ /** Original interface flags */
+ unsigned int flags;
+ /** Input queue */
+ struct ifqueue inq;
+ /** Output queue */
+ struct ifqueue outq;
+ /** Input task */
+ struct task tskin;
+ /** Output task */
+ struct task tskout;
+ /** The MAC address of the interface. */
+ RTMAC MacAddr;
+ /** @} */
+# elif defined(RT_OS_WINDOWS)
+ /** @name Windows instance data.
+ * @{ */
+ /** Filter driver device context. */
+ VBOXNETFLTWIN WinIf;
+
+ volatile uint32_t cModeNetFltRefs;
+ volatile uint32_t cModePassThruRefs;
+#ifndef VBOXNETFLT_NO_PACKET_QUEUE
+ /** Packet worker thread info */
+ PACKET_QUEUE_WORKER PacketQueueWorker;
+#endif
+ /** The MAC address of the interface. Caching MAC for performance reasons. */
+ RTMAC MacAddr;
+ /** mutex used to synchronize WinIf init/deinit */
+ RTSEMMUTEX hWinIfMutex;
+ /** @} */
+# else
+# error "PORTME"
+# endif
+ } s;
+#endif
+ /** Padding. */
+#if defined(RT_OS_WINDOWS)
+# if defined(VBOX_NETFLT_ONDEMAND_BIND)
+ uint8_t abPadding[192];
+# elif defined(VBOXNETADP)
+ uint8_t abPadding[256];
+# else
+ uint8_t abPadding[1024];
+# endif
+#elif defined(RT_OS_LINUX)
+ uint8_t abPadding[320];
+#elif defined(RT_OS_FREEBSD)
+ uint8_t abPadding[320];
+#else
+ uint8_t abPadding[128];
+#endif
+ } u;
+
+ /** The interface name. */
+ char szName[1];
+} VBOXNETFLTINS;
+/** Pointer to the instance data of a host network filter driver. */
+typedef struct VBOXNETFLTINS *PVBOXNETFLTINS;
+
+AssertCompileMemberAlignment(VBOXNETFLTINS, NanoTSLastRediscovery, 8);
+#ifdef VBOXNETFLT_OS_SPECFIC
+AssertCompile(RT_SIZEOFMEMB(VBOXNETFLTINS, u.s) <= RT_SIZEOFMEMB(VBOXNETFLTINS, u.abPadding));
+#endif
+
+
+/**
+ * The global data of the VBox filter driver.
+ *
+ * This contains the bit required for communicating with support driver, VBoxDrv
+ * (start out as SupDrv).
+ */
+typedef struct VBOXNETFLTGLOBALS
+{
+ /** Mutex protecting the list of instances and state changes. */
+ RTSEMFASTMUTEX hFastMtx;
+ /** Pointer to a list of instance data. */
+ PVBOXNETFLTINS pInstanceHead;
+
+ /** The INTNET trunk network interface factory. */
+ INTNETTRUNKFACTORY TrunkFactory;
+ /** The SUPDRV component factory registration. */
+ SUPDRVFACTORY SupDrvFactory;
+ /** The number of current factory references. */
+ int32_t volatile cFactoryRefs;
+ /** Whether the IDC connection is open or not.
+ * This is only for cleaning up correctly after the separate IDC init on Windows. */
+ bool fIDCOpen;
+ /** The SUPDRV IDC handle (opaque struct). */
+ SUPDRVIDCHANDLE SupDrvIDC;
+} VBOXNETFLTGLOBALS;
+
+
+DECLHIDDEN(int) vboxNetFltInitGlobalsAndIdc(PVBOXNETFLTGLOBALS pGlobals);
+DECLHIDDEN(int) vboxNetFltInitGlobals(PVBOXNETFLTGLOBALS pGlobals);
+DECLHIDDEN(int) vboxNetFltInitIdc(PVBOXNETFLTGLOBALS pGlobals);
+DECLHIDDEN(int) vboxNetFltTryDeleteIdcAndGlobals(PVBOXNETFLTGLOBALS pGlobals);
+DECLHIDDEN(void) vboxNetFltDeleteGlobals(PVBOXNETFLTGLOBALS pGlobals);
+DECLHIDDEN(int) vboxNetFltTryDeleteIdc(PVBOXNETFLTGLOBALS pGlobals);
+
+DECLHIDDEN(bool) vboxNetFltCanUnload(PVBOXNETFLTGLOBALS pGlobals);
+DECLHIDDEN(PVBOXNETFLTINS) vboxNetFltFindInstance(PVBOXNETFLTGLOBALS pGlobals, const char *pszName);
+
+DECL_HIDDEN_CALLBACK(void) vboxNetFltPortReleaseBusy(PINTNETTRUNKIFPORT pIfPort);
+DECLHIDDEN(void) vboxNetFltRetain(PVBOXNETFLTINS pThis, bool fBusy);
+DECLHIDDEN(bool) vboxNetFltTryRetainBusyActive(PVBOXNETFLTINS pThis);
+DECLHIDDEN(bool) vboxNetFltTryRetainBusyNotDisconnected(PVBOXNETFLTINS pThis);
+DECLHIDDEN(void) vboxNetFltRelease(PVBOXNETFLTINS pThis, bool fBusy);
+
+#ifdef VBOXNETFLT_STATIC_CONFIG
+DECLHIDDEN(int) vboxNetFltSearchCreateInstance(PVBOXNETFLTGLOBALS pGlobals, const char *pszName, PVBOXNETFLTINS *ppInstance, void * pContext);
+#endif
+
+
+
+/** @name The OS specific interface.
+ * @{ */
+/**
+ * Try rediscover the host interface.
+ *
+ * This is called periodically from the transmit path if we're marked as
+ * disconnected from the host. There is no chance of a race here.
+ *
+ * @returns true if the interface was successfully rediscovered and reattach,
+ * otherwise false.
+ * @param pThis The new instance.
+ */
+DECLHIDDEN(bool) vboxNetFltOsMaybeRediscovered(PVBOXNETFLTINS pThis);
+
+/**
+ * Transmits a frame.
+ *
+ * @return IPRT status code.
+ * @param pThis The new instance.
+ * @param pvIfData Pointer to the host-private interface data.
+ * @param pSG The (scatter/)gather list.
+ * @param fDst The destination mask. At least one bit will be set.
+ *
+ * @remarks Owns the out-bound trunk port semaphore.
+ */
+DECLHIDDEN(int) vboxNetFltPortOsXmit(PVBOXNETFLTINS pThis, void *pvIfData, PINTNETSG pSG, uint32_t fDst);
+
+/**
+ * This is called when activating or suspending the instance.
+ *
+ * Use this method to enable and disable promiscuous mode on
+ * the interface to prevent unnecessary interrupt load.
+ *
+ * It is only called when the state changes.
+ *
+ * @param pThis The instance.
+ * @param fActive Whether to active (@c true) or deactive.
+ *
+ * @remarks Owns the lock for the out-bound trunk port.
+ */
+DECLHIDDEN(void) vboxNetFltPortOsSetActive(PVBOXNETFLTINS pThis, bool fActive);
+
+/**
+ * This is called when a network interface has obtained a new MAC address.
+ *
+ * @param pThis The instance.
+ * @param pvIfData Pointer to the private interface data.
+ * @param pMac Pointer to the new MAC address.
+ */
+DECLHIDDEN(void) vboxNetFltPortOsNotifyMacAddress(PVBOXNETFLTINS pThis, void *pvIfData, PCRTMAC pMac);
+
+/**
+ * This is called when an interface is connected to the network.
+ *
+ * @return IPRT status code.
+ * @param pThis The instance.
+ * @param pvIf Pointer to the interface.
+ * @param ppvIfData Where to store the private interface data.
+ */
+DECLHIDDEN(int) vboxNetFltPortOsConnectInterface(PVBOXNETFLTINS pThis, void *pvIf, void **ppvIfData);
+
+/**
+ * This is called when a VM host disconnects from the network.
+ *
+ * @param pThis The instance.
+ * @param pvIfData Pointer to the private interface data.
+ */
+DECLHIDDEN(int) vboxNetFltPortOsDisconnectInterface(PVBOXNETFLTINS pThis, void *pvIfData);
+
+/**
+ * This is called to when disconnecting from a network.
+ *
+ * @return IPRT status code.
+ * @param pThis The new instance.
+ *
+ * @remarks May own the semaphores for the global list, the network lock and the out-bound trunk port.
+ */
+DECLHIDDEN(int) vboxNetFltOsDisconnectIt(PVBOXNETFLTINS pThis);
+
+/**
+ * This is called to when connecting to a network.
+ *
+ * @return IPRT status code.
+ * @param pThis The new instance.
+ *
+ * @remarks Owns the semaphores for the global list, the network lock and the out-bound trunk port.
+ */
+DECLHIDDEN(int) vboxNetFltOsConnectIt(PVBOXNETFLTINS pThis);
+
+/**
+ * Counter part to vboxNetFltOsInitInstance().
+ *
+ * @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) vboxNetFltOsDeleteInstance(PVBOXNETFLTINS pThis);
+
+/**
+ * This is called to attach to the actual host interface
+ * after linking the instance into the list.
+ *
+ * The MAC address as well promiscuousness and GSO capabilities should be
+ * reported by this function.
+ *
+ * @return IPRT status code.
+ * @param pThis The new instance.
+ * @param pvContext The user supplied context in the static config only.
+ * NULL in the dynamic config.
+ *
+ * @remarks Owns no locks.
+ */
+DECLHIDDEN(int) vboxNetFltOsInitInstance(PVBOXNETFLTINS pThis, void *pvContext);
+
+/**
+ * This is called to perform structure initializations.
+ *
+ * @return IPRT status code.
+ * @param pThis The new instance.
+ *
+ * @remarks Owns no locks.
+ */
+DECLHIDDEN(int) vboxNetFltOsPreInitInstance(PVBOXNETFLTINS pThis);
+/** @} */
+
+
+RT_C_DECLS_END
+
+#endif /* !VBOX_INCLUDED_SRC_VBoxNetFlt_VBoxNetFltInternal_h */
+
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/darwin/Info.plist b/src/VBox/HostDrivers/VBoxNetFlt/darwin/Info.plist
new file mode 100644
index 00000000..20338308
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/darwin/Info.plist
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key> <string>English</string>
+ <key>CFBundleExecutable</key> <string>VBoxNetFlt</string>
+ <key>CFBundleIdentifier</key> <string>org.virtualbox.kext.VBoxNetFlt</string>
+ <key>CFBundleInfoDictionaryVersion</key> <string>6.0</string>
+ <key>CFBundleName</key> <string>VBoxNetFlt</string>
+ <key>CFBundlePackageType</key> <string>KEXT</string>
+ <key>CFBundleSignature</key> <string>????</string>
+ <key>CFBundleGetInfoString</key> <string>@VBOX_PRODUCT@ @VBOX_VERSION_STRING@, © 2007-@VBOX_C_YEAR@ @VBOX_VENDOR@</string>
+ <key>CFBundleVersion</key> <string>@VBOX_VERSION_MAJOR@.@VBOX_VERSION_MINOR@.@VBOX_VERSION_BUILD@</string>
+ <key>CFBundleShortVersionString</key> <string>@VBOX_VERSION_MAJOR@.@VBOX_VERSION_MINOR@.@VBOX_VERSION_BUILD@</string>
+ <key>IOKitPersonalities</key>
+ <dict>
+ </dict>
+ <key>OSBundleLibraries</key>
+ <dict>
+ <key>com.apple.kpi.bsd</key> <string>8.8.1</string>
+ <key>com.apple.kpi.mach</key> <string>8.8.1</string>
+ <key>com.apple.kpi.libkern</key> <string>8.8.1</string>
+ <key>com.apple.kpi.unsupported</key> <string>8.8.1</string>
+ <key>com.apple.kpi.iokit</key> <string>8.8.1</string>
+ <key>org.virtualbox.kext.VBoxDrv</key> <string>@VBOX_VERSION_MAJOR@.@VBOX_VERSION_MINOR@.@VBOX_VERSION_BUILD@</string>
+ </dict>
+ <key>OSBundleLibraries_x86_64</key>
+ <dict>
+ <key>com.apple.kpi.bsd</key> <string>10.0.0d3</string>
+ <key>com.apple.kpi.mach</key> <string>10.0.0d3</string>
+ <key>com.apple.kpi.libkern</key> <string>10.0.0d3</string>
+ <key>com.apple.kpi.unsupported</key> <string>10.0.0d3</string>
+ <key>com.apple.kpi.iokit</key> <string>10.0.0d3</string>
+ <key>org.virtualbox.kext.VBoxDrv</key> <string>@VBOX_VERSION_MAJOR@.@VBOX_VERSION_MINOR@.@VBOX_VERSION_BUILD@</string>
+ </dict>
+</dict>
+</plist>
+
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/darwin/Makefile.kup b/src/VBox/HostDrivers/VBoxNetFlt/darwin/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/darwin/Makefile.kup
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/darwin/VBoxNetFlt-darwin.cpp b/src/VBox/HostDrivers/VBoxNetFlt/darwin/VBoxNetFlt-darwin.cpp
new file mode 100644
index 00000000..3a14a8c5
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/darwin/VBoxNetFlt-darwin.cpp
@@ -0,0 +1,1754 @@
+/* $Id: VBoxNetFlt-darwin.cpp $ */
+/** @file
+ * VBoxNetFlt - Network Filter Driver (Host), Darwin Specific Code.
+ */
+
+/*
+ * 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 <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_NET_FLT_DRV
+#include "../../../Runtime/r0drv/darwin/the-darwin-kernel.h"
+
+#include <VBox/log.h>
+#include <VBox/err.h>
+#include <VBox/intnetinline.h>
+#include <VBox/version.h>
+#include <iprt/initterm.h>
+#include <iprt/assert.h>
+#include <iprt/spinlock.h>
+#include <iprt/semaphore.h>
+#include <iprt/process.h>
+#include <iprt/alloc.h>
+#include <iprt/alloca.h>
+#include <iprt/time.h>
+#include <iprt/net.h>
+#include <iprt/thread.h>
+
+#include "../../darwin/VBoxNetSend.h"
+
+#include <mach/kmod.h>
+#include <sys/conf.h>
+#include <sys/errno.h>
+#include <sys/ioccom.h>
+#include <sys/filio.h>
+#include <sys/malloc.h>
+#include <sys/proc.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/kern_event.h>
+#if MAC_OS_X_VERSION_MIN_REQUIRED >= 101500 /* The 10.15 SDK has a slightly butchered API deprecation attempt. */
+# pragma clang diagnostic push
+# pragma clang diagnostic ignored "-Wmacro-redefined" /* Each header redefines __NKE_API_DEPRECATED. */
+# pragma clang diagnostic ignored "-Wmissing-declarations" /* Misplaced __NKE_API_DEPRECATED; in kpi_mbuf.h. */
+# include <sys/kpi_socket.h>
+# include <net/kpi_interface.h>
+# include <sys/kpi_mbuf.h>
+# include <net/kpi_interfacefilter.h>
+# pragma clang diagnostic pop
+#else /* < 10.15*/
+# include <sys/kpi_socket.h>
+# include <net/kpi_interface.h>
+RT_C_DECLS_BEGIN /* Buggy 10.4 headers, fixed in 10.5. */
+# include <sys/kpi_mbuf.h>
+# include <net/kpi_interfacefilter.h>
+RT_C_DECLS_END
+#endif /* < 10.15*/
+
+
+#include <net/if.h>
+#include <net/if_var.h>
+RT_C_DECLS_BEGIN
+#include <net/bpf.h>
+RT_C_DECLS_END
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet6/in6_var.h>
+
+#define VBOXNETFLT_OS_SPECFIC 1
+#include "../VBoxNetFltInternal.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** The maximum number of SG segments.
+ * Used to prevent stack overflow and similar bad stuff. */
+#define VBOXNETFLT_DARWIN_MAX_SEGS 32
+
+#if 0
+/** For testing extremely segmented frames. */
+#define VBOXNETFLT_DARWIN_TEST_SEG_SIZE 14
+#endif
+
+/* XXX: hidden undef #ifdef __APPLE__ */
+#define VBOX_IN_LOOPBACK(addr) (((addr) & IN_CLASSA_NET) == 0x7f000000)
+#define VBOX_IN_LINKLOCAL(addr) (((addr) & IN_CLASSB_NET) == 0xa9fe0000)
+
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+RT_C_DECLS_BEGIN
+static kern_return_t VBoxNetFltDarwinStart(struct kmod_info *pKModInfo, void *pvData);
+static kern_return_t VBoxNetFltDarwinStop(struct kmod_info *pKModInfo, void *pvData);
+
+static void vboxNetFltDarwinSysSockUpcall(socket_t pSysSock, void *pvData, int fWait);
+RT_C_DECLS_END
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * The mbuf tag data.
+ *
+ * We have to associate the ethernet header with each packet we're sending
+ * because things like icmp will inherit the tag it self so the tag along
+ * isn't sufficient to identify our mbufs. For the icmp scenario the ethernet
+ * header naturally changes before the packet is send pack, so let check it.
+ */
+typedef struct VBOXNETFLTTAG
+{
+ /** The ethernet header of the outgoing frame. */
+ RTNETETHERHDR EthHdr;
+} VBOXNETFLTTAG;
+/** Pointer to a VBoxNetFlt mbuf tag. */
+typedef VBOXNETFLTTAG *PVBOXNETFLTTAG;
+/** Pointer to a const VBoxNetFlt mbuf tag. */
+typedef VBOXNETFLTTAG const *PCVBOXNETFLTTAG;
+
+
+/*********************************************************************************************************************************
+* 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(VBoxNetFlt, VBOX_VERSION_STRING, _start, _stop)
+DECL_HIDDEN_DATA(kmod_start_func_t *) _realmain = VBoxNetFltDarwinStart;
+DECL_HIDDEN_DATA(kmod_stop_func_t *) _antimain = VBoxNetFltDarwinStop;
+DECL_HIDDEN_DATA(int) _kext_apple_cc = __APPLE_CC__;
+RT_C_DECLS_END
+
+
+/**
+ * The (common) global data.
+ */
+static VBOXNETFLTGLOBALS g_VBoxNetFltGlobals;
+
+/** The unique tag id for this module.
+ * This is basically a unique string hash that lives on until reboot.
+ * It is used for tagging mbufs. */
+static mbuf_tag_id_t g_idTag;
+
+/** The offset of the struct ifnet::if_pcount variable.
+ * @remarks Initial value is valid for Lion and earlier. We adjust it on attach
+ * for later releases. */
+static unsigned g_offIfNetPCount = sizeof(void *) * (1 /*if_softc*/ + 1 /*if_name*/ + 2 /*if_link*/ + 2 /*if_addrhead*/ + 1 /*if_check_multi*/)
+ + sizeof(u_long) /*if_refcnt*/;
+/** Macro for accessing ifnet::if_pcount. */
+#define VBOX_GET_PCOUNT(pIfNet) ( *(int *)((uintptr_t)pIfNet + g_offIfNetPCount) )
+/** The size of area of ifnet structure we try to locate if_pcount in. */
+#define VBOXNETFLT_DARWIN_IFNET_SIZE 256
+/** Indicates whether g_offIfNetPCount has been adjusted already (no point in
+ * doing it more than once). */
+static bool g_fNetPCountFound = false;
+
+
+/**
+ * Change the promiscuous setting and try spot the changed in @a pIfNet.
+ *
+ * @returns Offset of potential p_count field.
+ * @param pIfNet The interface we're attaching to.
+ * @param iPromisc Whether to enable (1) or disable (0) promiscuous mode.
+ *
+ * @note This implementation relies on if_pcount to be aligned on sizeof(int).
+ */
+static unsigned vboxNetFltDarwinSetAndDiff(ifnet_t pIfNet, int iPromisc)
+{
+ int aiSavedState[VBOXNETFLT_DARWIN_IFNET_SIZE / sizeof(int)];
+ memcpy(aiSavedState, pIfNet, sizeof(aiSavedState));
+
+ ifnet_set_promiscuous(pIfNet, iPromisc);
+
+ int const iDiff = iPromisc ? 1 : -1;
+
+ /*
+ * We assume that ifnet structure will never have less members in front of if_pcount
+ * than it used to have in Lion. If this turns out to be false assumption we will
+ * have to start from zero offset.
+ */
+ for (unsigned i = g_offIfNetPCount / sizeof(int); i < RT_ELEMENTS(aiSavedState); i++)
+ if (((int*)pIfNet)[i] - aiSavedState[i] == iDiff)
+ return i * sizeof(int);
+
+ return 0;
+}
+
+
+/**
+ * Detect and adjust the offset of ifnet::if_pcount.
+ *
+ * @param pIfNet The interface we're attaching to.
+ */
+static void vboxNetFltDarwinDetectPCountOffset(ifnet_t pIfNet)
+{
+ if (g_fNetPCountFound)
+ return;
+
+ /*
+ * It would be nice to use locking at this point, but it is not available via KPI.
+ * This is why we try several times. At each attempt we modify if_pcount four times
+ * to rule out false detections.
+ */
+ unsigned offTry1, offTry2, offTry3, offTry4;
+ for (int iAttempt = 0; iAttempt < 3; iAttempt++)
+ {
+ offTry1 = vboxNetFltDarwinSetAndDiff(pIfNet, 1);
+ offTry2 = vboxNetFltDarwinSetAndDiff(pIfNet, 1);
+ offTry3 = vboxNetFltDarwinSetAndDiff(pIfNet, 0);
+ offTry4 = vboxNetFltDarwinSetAndDiff(pIfNet, 0);
+ if (offTry1 == offTry2 && offTry2 == offTry3 && offTry3 == offTry4)
+ {
+ if (g_offIfNetPCount != offTry1)
+ {
+ Log(("VBoxNetFltDarwinDetectPCountOffset: Adjusted if_pcount offset to %x from %x.\n", offTry1, g_offIfNetPCount));
+ g_offIfNetPCount = offTry1;
+ g_fNetPCountFound = true;
+ }
+ break;
+ }
+ }
+
+ if (g_offIfNetPCount != offTry1)
+ LogRel(("VBoxNetFlt: Failed to detect promiscuous count, all traffic may reach wire (%x != %x).\n", g_offIfNetPCount, offTry1));
+}
+
+
+/**
+ * Start the kernel module.
+ */
+static kern_return_t VBoxNetFltDarwinStart(struct kmod_info *pKModInfo, void *pvData)
+{
+ RT_NOREF(pKModInfo, pvData);
+
+ /*
+ * Initialize IPRT and find our module tag id.
+ * (IPRT is shared with VBoxDrv, it creates the loggers.)
+ */
+ int rc = RTR0Init(0);
+ if (RT_SUCCESS(rc))
+ {
+ Log(("VBoxNetFltDarwinStart\n"));
+ errno_t err = mbuf_tag_id_find("org.VirtualBox.kext.VBoxFltDrv", &g_idTag);
+ if (!err)
+ {
+ /*
+ * Initialize the globals and connect to the support driver.
+ *
+ * This will call back vboxNetFltOsOpenSupDrv (and maybe vboxNetFltOsCloseSupDrv)
+ * for establishing the connect to the support driver.
+ */
+ memset(&g_VBoxNetFltGlobals, 0, sizeof(g_VBoxNetFltGlobals));
+ rc = vboxNetFltInitGlobalsAndIdc(&g_VBoxNetFltGlobals);
+ if (RT_SUCCESS(rc))
+ {
+ LogRel(("VBoxFltDrv: version " VBOX_VERSION_STRING " r%d\n", VBOX_SVN_REV));
+ return KMOD_RETURN_SUCCESS;
+ }
+
+ LogRel(("VBoxFltDrv: failed to initialize device extension (rc=%d)\n", rc));
+ }
+ else
+ LogRel(("VBoxFltDrv: mbuf_tag_id_find failed, err=%d\n", err));
+ RTR0Term();
+ }
+ else
+ printf("VBoxFltDrv: failed to initialize IPRT (rc=%d)\n", rc);
+
+ memset(&g_VBoxNetFltGlobals, 0, sizeof(g_VBoxNetFltGlobals));
+ return KMOD_RETURN_FAILURE;
+}
+
+
+/**
+ * Stop the kernel module.
+ */
+static kern_return_t VBoxNetFltDarwinStop(struct kmod_info *pKModInfo, void *pvData)
+{
+ RT_NOREF(pKModInfo, pvData);
+ Log(("VBoxNetFltDarwinStop\n"));
+
+ /*
+ * Refuse to unload if anyone is currently using the filter driver.
+ * This is important as I/O kit / xnu will to be able to do usage
+ * tracking for us!
+ */
+ int rc = vboxNetFltTryDeleteIdcAndGlobals(&g_VBoxNetFltGlobals);
+ if (RT_FAILURE(rc))
+ {
+ Log(("VBoxNetFltDarwinStop - failed, busy.\n"));
+ return KMOD_RETURN_FAILURE;
+ }
+
+ /*
+ * Undo the work done during start (in reverse order).
+ */
+ memset(&g_VBoxNetFltGlobals, 0, sizeof(g_VBoxNetFltGlobals));
+
+ RTR0Term();
+
+ return KMOD_RETURN_SUCCESS;
+}
+
+
+/**
+ * Reads and retains the host interface handle.
+ *
+ * @returns The handle, NULL if detached.
+ * @param pThis
+ */
+DECLINLINE(ifnet_t) vboxNetFltDarwinRetainIfNet(PVBOXNETFLTINS pThis)
+{
+ ifnet_t pIfNet = NULL;
+
+ /*
+ * Be careful here to avoid problems racing the detached callback.
+ */
+ RTSpinlockAcquire(pThis->hSpinlock);
+ if (!ASMAtomicUoReadBool(&pThis->fDisconnectedFromHost))
+ {
+ pIfNet = ASMAtomicUoReadPtrT(&pThis->u.s.pIfNet, ifnet_t);
+ if (pIfNet)
+ ifnet_reference(pIfNet);
+ }
+ RTSpinlockRelease(pThis->hSpinlock);
+
+ return pIfNet;
+}
+
+
+/**
+ * Release the host interface handle previously retained
+ * by vboxNetFltDarwinRetainIfNet.
+ *
+ * @param pThis The instance.
+ * @param pIfNet The vboxNetFltDarwinRetainIfNet return value, NULL is fine.
+ */
+DECLINLINE(void) vboxNetFltDarwinReleaseIfNet(PVBOXNETFLTINS pThis, ifnet_t pIfNet)
+{
+ NOREF(pThis);
+ if (pIfNet)
+ ifnet_release(pIfNet);
+}
+
+
+/**
+ * Checks whether this is an mbuf created by vboxNetFltDarwinMBufFromSG,
+ * i.e. a buffer which we're pushing and should be ignored by the filter callbacks.
+ *
+ * @returns true / false accordingly.
+ * @param pThis The instance.
+ * @param pMBuf The mbuf.
+ * @param pvFrame The frame pointer, optional.
+ */
+DECLINLINE(bool) vboxNetFltDarwinMBufIsOur(PVBOXNETFLTINS pThis, mbuf_t pMBuf, void *pvFrame)
+{
+ NOREF(pThis);
+
+ /*
+ * Lookup the tag set by vboxNetFltDarwinMBufFromSG.
+ */
+ PCVBOXNETFLTTAG pTagData;
+ size_t cbTagData;
+ errno_t err = mbuf_tag_find(pMBuf, g_idTag, 0 /* type */, &cbTagData, (void **)&pTagData);
+ if (err)
+ return false;
+ AssertReturn(cbTagData == sizeof(*pTagData), false);
+
+ /*
+ * Dig out the ethernet header from the mbuf.
+ */
+ PCRTNETETHERHDR pEthHdr = (PCRTNETETHERHDR)pvFrame;
+ if (!pEthHdr)
+ pEthHdr = (PCRTNETETHERHDR)mbuf_pkthdr_header(pMBuf);
+ if (!pEthHdr)
+ pEthHdr = (PCRTNETETHERHDR)mbuf_data(pMBuf);
+ /* ASSUMING that there is enough data to work on! */
+ if ( pEthHdr->DstMac.au8[0] != pTagData->EthHdr.DstMac.au8[0]
+ || pEthHdr->DstMac.au8[1] != pTagData->EthHdr.DstMac.au8[1]
+ || pEthHdr->DstMac.au8[2] != pTagData->EthHdr.DstMac.au8[2]
+ || pEthHdr->DstMac.au8[3] != pTagData->EthHdr.DstMac.au8[3]
+ || pEthHdr->DstMac.au8[4] != pTagData->EthHdr.DstMac.au8[4]
+ || pEthHdr->DstMac.au8[5] != pTagData->EthHdr.DstMac.au8[5]
+ || pEthHdr->SrcMac.au8[0] != pTagData->EthHdr.SrcMac.au8[0]
+ || pEthHdr->SrcMac.au8[1] != pTagData->EthHdr.SrcMac.au8[1]
+ || pEthHdr->SrcMac.au8[2] != pTagData->EthHdr.SrcMac.au8[2]
+ || pEthHdr->SrcMac.au8[3] != pTagData->EthHdr.SrcMac.au8[3]
+ || pEthHdr->SrcMac.au8[4] != pTagData->EthHdr.SrcMac.au8[4]
+ || pEthHdr->SrcMac.au8[5] != pTagData->EthHdr.SrcMac.au8[5]
+ || pEthHdr->EtherType != pTagData->EthHdr.EtherType)
+ {
+ Log3(("tagged, but the ethernet header has changed\n"));
+ return false;
+ }
+
+ return true;
+}
+
+
+/**
+ * Internal worker that create a darwin mbuf for a (scatter/)gather list.
+ *
+ * @returns Pointer to the mbuf.
+ * @param pThis The instance.
+ * @param pSG The (scatter/)gather list.
+ */
+static mbuf_t vboxNetFltDarwinMBufFromSG(PVBOXNETFLTINS pThis, PINTNETSG pSG)
+{
+ /// @todo future? mbuf_how_t How = preemption enabled ? MBUF_DONTWAIT : MBUF_WAITOK;
+ mbuf_how_t How = MBUF_WAITOK;
+
+ /*
+ * We need some way of getting back to our instance data when
+ * the mbuf is freed, so use pvUserData for this.
+ * -- this is not relevant anylonger! --
+ */
+ Assert(!pSG->pvUserData || pSG->pvUserData == pThis);
+ Assert(!pSG->pvUserData2);
+ pSG->pvUserData = pThis;
+
+ /*
+ * Allocate a packet and copy over the data.
+ *
+ * Using mbuf_attachcluster() here would've been nice but there are two
+ * issues with it: (1) it's 10.5.x only, and (2) the documentation indicates
+ * that it's not supposed to be used for really external buffers. The 2nd
+ * point might be argued against considering that the only m_clattach user
+ * is mallocs memory for the ext mbuf and not doing what's stated in the docs.
+ * However, it's hard to tell if these m_clattach buffers actually makes it
+ * to the NICs or not, and even if they did, the NIC would need the physical
+ * addresses for the pages they contain and might end up copying the data
+ * to a new mbuf anyway.
+ *
+ * So, in the end it's better to just do it the simple way that will work
+ * 100%, even if it involves some extra work (alloc + copy) we really wished
+ * to avoid.
+ *
+ * Note. We can't make use of the physical addresses on darwin because the
+ * way the mbuf / cluster stuff works (see mbuf_data_to_physical and
+ * mcl_to_paddr).
+ */
+ mbuf_t pPkt = NULL;
+ errno_t err = mbuf_allocpacket(How, pSG->cbTotal, NULL, &pPkt);
+ if (!err)
+ {
+ /* Skip zero sized memory buffers (paranoia). */
+ mbuf_t pCur = pPkt;
+ while (pCur && !mbuf_maxlen(pCur))
+ pCur = mbuf_next(pCur);
+ Assert(pCur);
+
+ /* Set the required packet header attributes. */
+ mbuf_pkthdr_setlen(pPkt, pSG->cbTotal);
+ mbuf_pkthdr_setheader(pPkt, mbuf_data(pCur));
+
+ /* Special case the single buffer copy. */
+ if ( mbuf_next(pCur)
+ && mbuf_maxlen(pCur) >= pSG->cbTotal)
+ {
+ mbuf_setlen(pCur, pSG->cbTotal);
+ IntNetSgRead(pSG, mbuf_data(pCur));
+ }
+ else
+ {
+ /* Multi buffer copying. */
+ size_t cbLeft = pSG->cbTotal;
+ size_t offSrc = 0;
+ while (cbLeft > 0 && pCur)
+ {
+ size_t cb = mbuf_maxlen(pCur);
+ if (cb > cbLeft)
+ cb = cbLeft;
+ mbuf_setlen(pCur, cb);
+ IntNetSgReadEx(pSG, offSrc, cb, mbuf_data(pCur));
+
+ /* advance */
+ offSrc += cb;
+ cbLeft -= cb;
+ pCur = mbuf_next(pCur);
+ }
+ Assert(cbLeft == 0);
+ }
+ if (!err)
+ {
+ /*
+ * Tag the packet and return successfully.
+ */
+ PVBOXNETFLTTAG pTagData;
+ err = mbuf_tag_allocate(pPkt, g_idTag, 0 /* type */, sizeof(VBOXNETFLTTAG) /* tag len */, How, (void **)&pTagData);
+ if (!err)
+ {
+ Assert(pSG->aSegs[0].cb >= sizeof(pTagData->EthHdr));
+ memcpy(&pTagData->EthHdr, pSG->aSegs[0].pv, sizeof(pTagData->EthHdr));
+ return pPkt;
+ }
+
+ /* bailout: */
+ AssertMsg(err == ENOMEM || err == EWOULDBLOCK, ("err=%d\n", err));
+ }
+
+ mbuf_freem(pPkt);
+ }
+ else
+ AssertMsg(err == ENOMEM || err == EWOULDBLOCK, ("err=%d\n", err));
+ pSG->pvUserData = NULL;
+
+ return NULL;
+}
+
+
+/**
+ * Calculates the number of segments required to represent the mbuf.
+ *
+ * @returns Number of segments.
+ * @param pThis The instance.
+ * @param pMBuf The mbuf.
+ * @param pvFrame The frame pointer, optional.
+ */
+DECLINLINE(unsigned) vboxNetFltDarwinMBufCalcSGSegs(PVBOXNETFLTINS pThis, mbuf_t pMBuf, void *pvFrame)
+{
+ NOREF(pThis);
+
+ /*
+ * Count the buffers in the chain.
+ */
+ unsigned cSegs = 0;
+ for (mbuf_t pCur = pMBuf; pCur; pCur = mbuf_next(pCur))
+ if (mbuf_len(pCur))
+ cSegs++;
+ else if ( !cSegs
+ && pvFrame
+ && (uintptr_t)pvFrame - (uintptr_t)mbuf_datastart(pMBuf) < mbuf_maxlen(pMBuf))
+ cSegs++;
+
+#ifdef PADD_RUNT_FRAMES_FROM_HOST
+ /*
+ * Add one buffer if the total is less than the ethernet minimum 60 bytes.
+ * This may allocate a segment too much if the ethernet header is separated,
+ * but that shouldn't harm us much.
+ */
+ if (mbuf_pkthdr_len(pMBuf) < 60)
+ cSegs++;
+#endif
+
+#ifdef VBOXNETFLT_DARWIN_TEST_SEG_SIZE
+ /* maximize the number of segments. */
+ cSegs = RT_MAX(VBOXNETFLT_DARWIN_MAX_SEGS - 1, cSegs);
+#endif
+
+ return cSegs ? cSegs : 1;
+}
+
+
+/**
+ * Initializes a SG list from an mbuf.
+ *
+ * @param pThis The instance.
+ * @param pMBuf The mbuf.
+ * @param pSG The SG.
+ * @param pvFrame The frame pointer, optional.
+ * @param cSegs The number of segments allocated for the SG.
+ * This should match the number in the mbuf exactly!
+ * @param fSrc The source of the frame.
+ */
+DECLINLINE(void) vboxNetFltDarwinMBufToSG(PVBOXNETFLTINS pThis, mbuf_t pMBuf, void *pvFrame, PINTNETSG pSG, unsigned cSegs,
+ uint32_t fSrc)
+{
+ RT_NOREF(pThis, fSrc);
+
+ /*
+ * Walk the chain and convert the buffers to segments. Works INTNETSG::cbTotal.
+ */
+ unsigned iSeg = 0;
+ IntNetSgInitTempSegs(pSG, 0 /*cbTotal*/, cSegs, 0 /*cSegsUsed*/);
+ for (mbuf_t pCur = pMBuf; pCur; pCur = mbuf_next(pCur))
+ {
+ size_t cbSeg = mbuf_len(pCur);
+ if (cbSeg)
+ {
+ void *pvSeg = mbuf_data(pCur);
+
+ /* deal with pvFrame */
+ if (!iSeg && pvFrame && pvFrame != pvSeg)
+ {
+ void *pvStart = mbuf_datastart(pMBuf);
+ uintptr_t offSeg = (uintptr_t)pvSeg - (uintptr_t)pvStart;
+ uintptr_t offSegEnd = offSeg + cbSeg;
+ Assert(pvStart && pvSeg && offSeg < mbuf_maxlen(pMBuf) && offSegEnd <= mbuf_maxlen(pMBuf)); NOREF(offSegEnd);
+ uintptr_t offFrame = (uintptr_t)pvFrame - (uintptr_t)pvStart;
+ if (RT_LIKELY(offFrame < offSeg))
+ {
+ pvSeg = pvFrame;
+ cbSeg += offSeg - offFrame;
+ }
+ else
+ AssertMsgFailed(("pvFrame=%p pvStart=%p pvSeg=%p offSeg=%p cbSeg=%#zx offSegEnd=%p offFrame=%p maxlen=%#zx\n",
+ pvFrame, pvStart, pvSeg, offSeg, cbSeg, offSegEnd, offFrame, mbuf_maxlen(pMBuf)));
+ pvFrame = NULL;
+ }
+
+ AssertBreak(iSeg < cSegs);
+ pSG->cbTotal += cbSeg;
+ pSG->aSegs[iSeg].cb = cbSeg;
+ pSG->aSegs[iSeg].pv = pvSeg;
+ pSG->aSegs[iSeg].Phys = NIL_RTHCPHYS;
+ iSeg++;
+ }
+ /* The pvFrame might be in a now empty buffer. */
+ else if ( !iSeg
+ && pvFrame
+ && (uintptr_t)pvFrame - (uintptr_t)mbuf_datastart(pMBuf) < mbuf_maxlen(pMBuf))
+ {
+ cbSeg = (uintptr_t)mbuf_datastart(pMBuf) + mbuf_maxlen(pMBuf) - (uintptr_t)pvFrame;
+ pSG->cbTotal += cbSeg;
+ pSG->aSegs[iSeg].cb = cbSeg;
+ pSG->aSegs[iSeg].pv = pvFrame;
+ pSG->aSegs[iSeg].Phys = NIL_RTHCPHYS;
+ iSeg++;
+ pvFrame = NULL;
+ }
+ }
+
+ Assert(iSeg && iSeg <= cSegs);
+ pSG->cSegsUsed = iSeg;
+
+#ifdef PADD_RUNT_FRAMES_FROM_HOST
+ /*
+ * Add a trailer if the frame is too small.
+ *
+ * Since we're getting to the packet before it is framed, it has not
+ * yet been padded. The current solution is to add a segment pointing
+ * to a buffer containing all zeros and pray that works for all frames...
+ */
+ if (pSG->cbTotal < 60 && (fSrc == INTNETTRUNKDIR_HOST))
+ {
+ AssertReturnVoid(iSeg < cSegs);
+
+ static uint8_t const s_abZero[128] = {0};
+ pSG->aSegs[iSeg].Phys = NIL_RTHCPHYS;
+ pSG->aSegs[iSeg].pv = (void *)&s_abZero[0];
+ pSG->aSegs[iSeg].cb = 60 - pSG->cbTotal;
+ pSG->cbTotal = 60;
+ pSG->cSegsUsed++;
+ }
+#endif
+
+#ifdef VBOXNETFLT_DARWIN_TEST_SEG_SIZE
+ /*
+ * Redistribute the segments.
+ */
+ if (pSG->cSegsUsed < pSG->cSegsAlloc)
+ {
+ /* copy the segments to the end. */
+ int iSrc = pSG->cSegsUsed;
+ int iDst = pSG->cSegsAlloc;
+ while (iSrc > 0)
+ {
+ iDst--;
+ iSrc--;
+ pSG->aSegs[iDst] = pSG->aSegs[iSrc];
+ }
+
+ /* create small segments from the start. */
+ pSG->cSegsUsed = pSG->cSegsAlloc;
+ iSrc = iDst;
+ iDst = 0;
+ while ( iDst < iSrc
+ && iDst < pSG->cSegsAlloc)
+ {
+ pSG->aSegs[iDst].Phys = NIL_RTHCPHYS;
+ pSG->aSegs[iDst].pv = pSG->aSegs[iSrc].pv;
+ pSG->aSegs[iDst].cb = RT_MIN(pSG->aSegs[iSrc].cb, VBOXNETFLT_DARWIN_TEST_SEG_SIZE);
+ if (pSG->aSegs[iDst].cb != pSG->aSegs[iSrc].cb)
+ {
+ pSG->aSegs[iSrc].cb -= pSG->aSegs[iDst].cb;
+ pSG->aSegs[iSrc].pv = (uint8_t *)pSG->aSegs[iSrc].pv + pSG->aSegs[iDst].cb;
+ }
+ else if (++iSrc >= pSG->cSegsAlloc)
+ {
+ pSG->cSegsUsed = iDst + 1;
+ break;
+ }
+ iDst++;
+ }
+ }
+#endif
+
+ AssertMsg(!pvFrame, ("pvFrame=%p pMBuf=%p iSeg=%d\n", pvFrame, pMBuf, iSeg));
+}
+
+
+/**
+ * Helper for determining whether the host wants the interface to be
+ * promiscuous.
+ */
+static bool vboxNetFltDarwinIsPromiscuous(PVBOXNETFLTINS pThis)
+{
+ bool fRc = false;
+ ifnet_t pIfNet = vboxNetFltDarwinRetainIfNet(pThis);
+ if (pIfNet)
+ {
+ /* gather the data */
+ uint16_t fIf = ifnet_flags(pIfNet);
+ unsigned cPromisc = VBOX_GET_PCOUNT(pIfNet);
+ bool fSetPromiscuous = ASMAtomicUoReadBool(&pThis->u.s.fSetPromiscuous);
+ vboxNetFltDarwinReleaseIfNet(pThis, pIfNet);
+
+ /* calc the return. */
+ fRc = (fIf & IFF_PROMISC)
+ && cPromisc > fSetPromiscuous;
+ }
+ return fRc;
+}
+
+
+
+/**
+ *
+ * @see iff_detached_func in the darwin kpi.
+ */
+static void vboxNetFltDarwinIffDetached(void *pvThis, ifnet_t pIfNet)
+{
+ PVBOXNETFLTINS pThis = (PVBOXNETFLTINS)pvThis;
+ uint64_t NanoTS = RTTimeSystemNanoTS();
+ LogFlow(("vboxNetFltDarwinIffDetached: pThis=%p NanoTS=%RU64 (%d)\n",
+ pThis, NanoTS, RT_VALID_PTR(pIfNet) ? VBOX_GET_PCOUNT(pIfNet) : -1));
+
+ Assert(!pThis->fDisconnectedFromHost);
+ Assert(!pThis->fRediscoveryPending);
+
+ /*
+ * If we've put it into promiscuous mode, undo that now. If we don't
+ * the if_pcount will go all wrong when it's replugged.
+ */
+ if (ASMAtomicXchgBool(&pThis->u.s.fSetPromiscuous, false))
+ ifnet_set_promiscuous(pIfNet, 0);
+
+ /*
+ * We carefully take the spinlock and increase the interface reference
+ * behind it in order to avoid problematic races with the detached callback.
+ */
+ RTSpinlockAcquire(pThis->hSpinlock);
+
+ pIfNet = ASMAtomicUoReadPtrT(&pThis->u.s.pIfNet, ifnet_t);
+ int cPromisc = RT_VALID_PTR(pIfNet) ? VBOX_GET_PCOUNT(pIfNet) : - 1;
+
+ ASMAtomicUoWriteNullPtr(&pThis->u.s.pIfNet);
+ ASMAtomicUoWriteNullPtr(&pThis->u.s.pIfFilter);
+ ASMAtomicWriteBool(&pThis->u.s.fNeedSetPromiscuous, false);
+ pThis->u.s.fSetPromiscuous = false;
+ ASMAtomicUoWriteU64(&pThis->NanoTSLastRediscovery, NanoTS);
+ ASMAtomicUoWriteBool(&pThis->fRediscoveryPending, false);
+ ASMAtomicWriteBool(&pThis->fDisconnectedFromHost, true);
+
+ RTSpinlockRelease(pThis->hSpinlock);
+
+ if (pIfNet)
+ ifnet_release(pIfNet);
+ LogRel(("VBoxNetFlt: was detached from '%s' (%d)\n", pThis->szName, cPromisc));
+}
+
+
+/**
+ *
+ * @see iff_ioctl_func in the darwin kpi.
+ */
+static errno_t vboxNetFltDarwinIffIoCtl(void *pvThis, ifnet_t pIfNet, protocol_family_t eProtocol, u_long uCmd, void *pvArg)
+{
+ RT_NOREF(pIfNet);
+ PVBOXNETFLTINS pThis = (PVBOXNETFLTINS)pvThis;
+ LogFlow(("vboxNetFltDarwinIffIoCtl: pThis=%p uCmd=%lx\n", pThis, uCmd));
+
+ /*
+ * Update fOtherPromiscuous.
+ */
+ /** @todo we'll have to find the offset of if_pcount to get this right! */
+ //if (uCmd == SIOCSIFFLAGS)
+ //{
+ //
+ //}
+
+ /*
+ * We didn't handle it, continue processing.
+ */
+ NOREF(pThis);
+ NOREF(eProtocol);
+ NOREF(uCmd);
+ NOREF(pvArg);
+ return EOPNOTSUPP;
+}
+
+
+/**
+ *
+ * @see iff_event_func in the darwin kpi.
+ */
+static void vboxNetFltDarwinIffEvent(void *pvThis, ifnet_t pIfNet, protocol_family_t eProtocol, const struct kev_msg *pEvMsg)
+{
+ PVBOXNETFLTINS pThis = (PVBOXNETFLTINS)pvThis;
+ LogFlow(("vboxNetFltDarwinIffEvent: pThis=%p\n", pThis));
+
+ NOREF(pThis);
+ NOREF(pIfNet);
+ NOREF(eProtocol);
+ NOREF(pEvMsg);
+
+ /*
+ * Watch out for the interface going online / offline.
+ */
+ if ( RT_VALID_PTR(pThis)
+ && RT_VALID_PTR(pEvMsg)
+ && pEvMsg->vendor_code == KEV_VENDOR_APPLE
+ && pEvMsg->kev_class == KEV_NETWORK_CLASS
+ && pEvMsg->kev_subclass == KEV_DL_SUBCLASS)
+ {
+ if (pThis->u.s.pIfNet == pIfNet)
+ {
+ if (pEvMsg->event_code == KEV_DL_LINK_ON)
+ {
+ if (ASMAtomicUoReadBool(&pThis->u.s.fNeedSetPromiscuous))
+ {
+ /* failed to bring it online. */
+ errno_t err = ifnet_set_promiscuous(pIfNet, 1);
+ if (!err)
+ {
+ ASMAtomicWriteBool(&pThis->u.s.fSetPromiscuous, true);
+ ASMAtomicWriteBool(&pThis->u.s.fNeedSetPromiscuous, false);
+ Log(("vboxNetFltDarwinIffEvent: enabled promiscuous mode on %s (%d)\n", pThis->szName, VBOX_GET_PCOUNT(pIfNet)));
+ }
+ else
+ Log(("vboxNetFltDarwinIffEvent: ifnet_set_promiscuous failed on %s, err=%d (%d)\n", pThis->szName, err, VBOX_GET_PCOUNT(pIfNet)));
+ }
+ else if ( ASMAtomicUoReadBool(&pThis->u.s.fSetPromiscuous)
+ && !(ifnet_flags(pIfNet) & IFF_PROMISC))
+ {
+ /* Try fix the inconsistency. */
+ errno_t err = ifnet_set_flags(pIfNet, IFF_PROMISC, IFF_PROMISC);
+ if (!err)
+ err = ifnet_ioctl(pIfNet, 0, SIOCSIFFLAGS, NULL);
+ if (!err && (ifnet_flags(pIfNet) & IFF_PROMISC))
+ Log(("vboxNetFltDarwinIffEvent: fixed IFF_PROMISC on %s (%d)\n", pThis->szName, VBOX_GET_PCOUNT(pIfNet)));
+ else
+ Log(("vboxNetFltDarwinIffEvent: failed to fix IFF_PROMISC on %s, err=%d flags=%#x (%d)\n",
+ pThis->szName, err, ifnet_flags(pIfNet), VBOX_GET_PCOUNT(pIfNet)));
+ }
+ else
+ Log(("vboxNetFltDarwinIffEvent: online, '%s'. flags=%#x (%d)\n", pThis->szName, ifnet_flags(pIfNet), VBOX_GET_PCOUNT(pIfNet)));
+ }
+ else if (pEvMsg->event_code == KEV_DL_LINK_OFF)
+ Log(("vboxNetFltDarwinIffEvent: %s goes down (%d)\n", pThis->szName, VBOX_GET_PCOUNT(pIfNet)));
+/** @todo KEV_DL_LINK_ADDRESS_CHANGED -> pfnReportMacAddress */
+/** @todo KEV_DL_SIFFLAGS -> pfnReportPromiscuousMode */
+ }
+ else
+ Log(("vboxNetFltDarwinIffEvent: pThis->u.s.pIfNet=%p pIfNet=%p (%d)\n", pThis->u.s.pIfNet, pIfNet, RT_VALID_PTR(pIfNet) ? VBOX_GET_PCOUNT(pIfNet) : -1));
+ }
+ else if (RT_VALID_PTR(pEvMsg))
+ Log(("vboxNetFltDarwinIffEvent: vendor_code=%#x kev_class=%#x kev_subclass=%#x event_code=%#x\n",
+ pEvMsg->vendor_code, pEvMsg->kev_class, pEvMsg->kev_subclass, pEvMsg->event_code));
+}
+
+
+/**
+ * Internal worker for vboxNetFltDarwinIffInput and vboxNetFltDarwinIffOutput,
+ *
+ * @returns 0 or EJUSTRETURN.
+ * @param pThis The instance.
+ * @param pMBuf The mbuf.
+ * @param pvFrame The start of the frame, optional.
+ * @param fSrc Where the packet (allegedly) comes from, one INTNETTRUNKDIR_* value.
+ * @param eProtocol The protocol.
+ */
+static errno_t vboxNetFltDarwinIffInputOutputWorker(PVBOXNETFLTINS pThis, mbuf_t pMBuf, void *pvFrame,
+ uint32_t fSrc, protocol_family_t eProtocol)
+{
+ /*
+ * Drop it immediately?
+ */
+ Log2(("vboxNetFltDarwinIffInputOutputWorker: pThis=%p pMBuf=%p pvFrame=%p fSrc=%#x cbPkt=%x\n",
+ pThis, pMBuf, pvFrame, fSrc, pMBuf ? mbuf_pkthdr_len(pMBuf) : -1));
+ if (!pMBuf)
+ return 0;
+#if 0 /* debugging lost icmp packets */
+ if (mbuf_pkthdr_len(pMBuf) > 0x300)
+ {
+ uint8_t *pb = (uint8_t *)(pvFrame ? pvFrame : mbuf_data(pMBuf));
+ Log3(("D=%.6Rhxs S=%.6Rhxs T=%04x IFF\n", pb, pb + 6, RT_BE2H_U16(*(uint16_t *)(pb + 12))));
+ }
+#endif
+ if (vboxNetFltDarwinMBufIsOur(pThis, pMBuf, pvFrame))
+ return 0;
+
+ /*
+ * Active? Retain the instance and increment the busy counter.
+ */
+ if (!vboxNetFltTryRetainBusyActive(pThis))
+ return 0;
+
+ /*
+ * Finalize out-bound packets since the stack puts off finalizing
+ * TCP/IP checksums as long as possible.
+ * ASSUMES this only applies to outbound IP packets.
+ */
+ if (fSrc == INTNETTRUNKDIR_HOST)
+ {
+ Assert(!pvFrame);
+ mbuf_outbound_finalize(pMBuf, eProtocol, sizeof(RTNETETHERHDR));
+ }
+
+ /*
+ * Create a (scatter/)gather list for the mbuf and feed it to the internal network.
+ */
+ bool fDropIt = false;
+ unsigned cSegs = vboxNetFltDarwinMBufCalcSGSegs(pThis, pMBuf, pvFrame);
+ if (cSegs < VBOXNETFLT_DARWIN_MAX_SEGS)
+ {
+ PINTNETSG pSG = (PINTNETSG)alloca(RT_UOFFSETOF_DYN(INTNETSG, aSegs[cSegs]));
+ vboxNetFltDarwinMBufToSG(pThis, pMBuf, pvFrame, pSG, cSegs, fSrc);
+
+ fDropIt = pThis->pSwitchPort->pfnRecv(pThis->pSwitchPort, NULL /* pvIf */, pSG, fSrc);
+ if (fDropIt)
+ {
+ /*
+ * If the interface is in promiscuous mode we should let
+ * all inbound packets (this one was for a bridged guest)
+ * reach the driver as it passes them to tap callbacks in
+ * order for BPF to work properly.
+ */
+ if ( fSrc == INTNETTRUNKDIR_WIRE
+ && vboxNetFltDarwinIsPromiscuous(pThis))
+ {
+ fDropIt = false;
+ }
+
+ /*
+ * A packet from the host to a guest. As we won't pass it
+ * to the drvier/wire we need to feed it to bpf ourselves.
+ *
+ * XXX: TODO: bpf should be done before; use pfnPreRecv?
+ */
+ if (fSrc == INTNETTRUNKDIR_HOST)
+ {
+ bpf_tap_out(pThis->u.s.pIfNet, DLT_EN10MB, pMBuf, NULL, 0);
+ ifnet_stat_increment_out(pThis->u.s.pIfNet, 1, mbuf_len(pMBuf), 0);
+ }
+ }
+ }
+
+ vboxNetFltRelease(pThis, true /* fBusy */);
+
+ if (fDropIt)
+ {
+ mbuf_freem(pMBuf);
+ return EJUSTRETURN;
+ }
+ return 0;
+}
+
+
+/**
+ * From the host.
+ *
+ * @see iff_output_func in the darwin kpi.
+ */
+static errno_t vboxNetFltDarwinIffOutput(void *pvThis, ifnet_t pIfNet, protocol_family_t eProtocol, mbuf_t *ppMBuf)
+{
+ /** @todo there was some note about the ethernet header here or something like that... */
+
+ NOREF(eProtocol);
+ NOREF(pIfNet);
+ return vboxNetFltDarwinIffInputOutputWorker((PVBOXNETFLTINS)pvThis, *ppMBuf, NULL, INTNETTRUNKDIR_HOST, eProtocol);
+}
+
+
+/**
+ * From the wire.
+ *
+ * @see iff_input_func in the darwin kpi.
+ */
+static errno_t vboxNetFltDarwinIffInput(void *pvThis, ifnet_t pIfNet, protocol_family_t eProtocol, mbuf_t *ppMBuf, char **ppchFrame)
+{
+ RT_NOREF(eProtocol, pIfNet);
+ return vboxNetFltDarwinIffInputOutputWorker((PVBOXNETFLTINS)pvThis, *ppMBuf, *ppchFrame, INTNETTRUNKDIR_WIRE, eProtocol);
+}
+
+
+/** A worker thread for vboxNetFltSendDummy(). */
+static DECLCALLBACK(int) vboxNetFltSendDummyWorker(RTTHREAD hThreadSelf, void *pvUser)
+{
+ RT_NOREF(hThreadSelf);
+ Assert(pvUser);
+ ifnet_t pIfNet = (ifnet_t)pvUser;
+ return VBoxNetSendDummy(pIfNet);
+}
+
+
+/**
+ * Prevent GUI icon freeze issue when VirtualBoxVM process terminates.
+ *
+ * This function is a workaround for stuck-in-dock issue. The idea here is to
+ * send a dummy packet to an interface from the context of a kernel thread.
+ * Therefore, an XNU's receive thread (which is created as a result if we are
+ * the first who is communicating with the interface) will be associated with
+ * the kernel thread instead of VirtualBoxVM process.
+ *
+ * @param pIfNet Interface to be used to send data.
+ */
+static void vboxNetFltSendDummy(ifnet_t pIfNet)
+{
+ RTTHREAD hThread;
+ int rc = RTThreadCreate(&hThread, vboxNetFltSendDummyWorker, (void *)pIfNet, 0,
+ RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "DummyThread");
+ if (RT_SUCCESS(rc))
+ {
+ RTThreadWait(hThread, RT_INDEFINITE_WAIT, NULL);
+ LogFlow(("vboxNetFltSendDummy: a dummy packet has been successfully sent in order to prevent stuck-in-dock issue\n"));
+ }
+ else
+ LogFlow(("vboxNetFltSendDummy: unable to send dummy packet in order to prevent stuck-in-dock issue\n"));
+}
+
+
+/**
+ * Internal worker for vboxNetFltOsInitInstance and vboxNetFltOsMaybeRediscovered.
+ *
+ * @returns VBox status code.
+ * @param pThis The instance.
+ * @param fRediscovery If set we're doing a rediscovery attempt, so, don't
+ * flood the release log.
+ */
+static int vboxNetFltDarwinAttachToInterface(PVBOXNETFLTINS pThis, bool fRediscovery)
+{
+ LogFlow(("vboxNetFltDarwinAttachToInterface: pThis=%p (%s)\n", pThis, pThis->szName));
+ IPRT_DARWIN_SAVE_EFL_AC();
+
+ /*
+ * Locate the interface first.
+ *
+ * The pIfNet member is updated before iflt_attach is called and used
+ * to deal with the hypothetical case where someone rips out the
+ * interface immediately after our iflt_attach call.
+ */
+ ifnet_t pIfNet = NULL;
+ errno_t err = ifnet_find_by_name(pThis->szName, &pIfNet);
+ if (err)
+ {
+ Assert(err == ENXIO);
+ if (!fRediscovery)
+ LogRel(("VBoxFltDrv: failed to find ifnet '%s' (err=%d)\n", pThis->szName, err));
+ else
+ Log(("VBoxFltDrv: failed to find ifnet '%s' (err=%d)\n", pThis->szName, err));
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return VERR_INTNET_FLT_IF_NOT_FOUND;
+ }
+
+ AssertCompileMemberAlignment(VBOXNETFLTINS, u.s.pIfNet, ARCH_BITS / 8);
+ AssertMsg(!((uintptr_t)&pThis->u.s.pIfNet & (ARCH_BITS / 8 - 1)), ("pThis=%p\n", pThis));
+ RTSpinlockAcquire(pThis->hSpinlock);
+ ASMAtomicUoWritePtr(&pThis->u.s.pIfNet, pIfNet);
+ RTSpinlockRelease(pThis->hSpinlock);
+
+ /* Adjust g_offIfNetPCount as it varies for different versions of xnu. */
+ vboxNetFltDarwinDetectPCountOffset(pIfNet);
+
+ /* Prevent stuck-in-dock issue by associating interface receive thread with kernel thread. */
+ vboxNetFltSendDummy(pIfNet);
+
+ /*
+ * Get the mac address while we still have a valid ifnet reference.
+ */
+ err = ifnet_lladdr_copy_bytes(pIfNet, &pThis->u.s.MacAddr, sizeof(pThis->u.s.MacAddr));
+ if (!err)
+ {
+ /*
+ * Try attach the filter.
+ */
+ struct iff_filter RegRec;
+ RegRec.iff_cookie = pThis;
+ RegRec.iff_name = "VBoxNetFlt";
+ RegRec.iff_protocol = 0;
+ RegRec.iff_input = vboxNetFltDarwinIffInput;
+ RegRec.iff_output = vboxNetFltDarwinIffOutput;
+ RegRec.iff_event = vboxNetFltDarwinIffEvent;
+ RegRec.iff_ioctl = vboxNetFltDarwinIffIoCtl;
+ RegRec.iff_detached = vboxNetFltDarwinIffDetached;
+ interface_filter_t pIfFilter = NULL;
+ err = iflt_attach(pIfNet, &RegRec, &pIfFilter);
+ Assert(err || pIfFilter);
+
+ RTSpinlockAcquire(pThis->hSpinlock);
+ pIfNet = ASMAtomicUoReadPtrT(&pThis->u.s.pIfNet, ifnet_t);
+ if (pIfNet && !err)
+ {
+ ASMAtomicUoWriteBool(&pThis->fDisconnectedFromHost, false);
+ ASMAtomicUoWritePtr(&pThis->u.s.pIfFilter, pIfFilter);
+ pIfNet = NULL; /* don't dereference it */
+ }
+ RTSpinlockRelease(pThis->hSpinlock);
+
+ /* Report capabilities. */
+ if ( !pIfNet
+ && vboxNetFltTryRetainBusyNotDisconnected(pThis))
+ {
+ Assert(pThis->pSwitchPort);
+ pThis->pSwitchPort->pfnReportMacAddress(pThis->pSwitchPort, &pThis->u.s.MacAddr);
+#if 0
+ /*
+ * XXX: Don't tell SrvIntNetR0 if the interface is
+ * promiscuous, because there's no code yet to update that
+ * information and we don't want it stuck, spamming all
+ * traffic to the host.
+ */
+ pThis->pSwitchPort->pfnReportPromiscuousMode(pThis->pSwitchPort, vboxNetFltDarwinIsPromiscuous(pThis));
+#endif
+ pThis->pSwitchPort->pfnReportGsoCapabilities(pThis->pSwitchPort, 0, INTNETTRUNKDIR_WIRE | INTNETTRUNKDIR_HOST);
+ pThis->pSwitchPort->pfnReportNoPreemptDsts(pThis->pSwitchPort, 0 /* none */);
+ vboxNetFltRelease(pThis, true /*fBusy*/);
+ }
+ }
+
+ /* Release the interface on failure. */
+ if (pIfNet)
+ ifnet_release(pIfNet);
+
+ int rc = RTErrConvertFromErrno(err);
+ if (RT_SUCCESS(rc))
+ LogRel(("VBoxFltDrv: attached to '%s' / %RTmac\n", pThis->szName, &pThis->u.s.MacAddr));
+ else
+ LogRel(("VBoxFltDrv: failed to attach to ifnet '%s' (err=%d)\n", pThis->szName, err));
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return rc;
+}
+
+
+bool vboxNetFltOsMaybeRediscovered(PVBOXNETFLTINS pThis)
+{
+ vboxNetFltDarwinAttachToInterface(pThis, true /* fRediscovery */);
+ return !ASMAtomicUoReadBool(&pThis->fDisconnectedFromHost);
+}
+
+
+int vboxNetFltPortOsXmit(PVBOXNETFLTINS pThis, void *pvIfData, PINTNETSG pSG, uint32_t fDst)
+{
+ IPRT_DARWIN_SAVE_EFL_AC();
+ NOREF(pvIfData);
+
+ int rc = VINF_SUCCESS;
+ ifnet_t pIfNet = vboxNetFltDarwinRetainIfNet(pThis);
+ if (pIfNet)
+ {
+ /*
+ * Create a mbuf for the gather list and push it onto the wire.
+ * BPF tap and stats will be taken care of by the driver.
+ */
+ if (fDst & INTNETTRUNKDIR_WIRE)
+ {
+ mbuf_t pMBuf = vboxNetFltDarwinMBufFromSG(pThis, pSG);
+ if (pMBuf)
+ {
+ errno_t err = ifnet_output_raw(pIfNet, PF_LINK, pMBuf);
+ if (err)
+ rc = RTErrConvertFromErrno(err);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+
+ /*
+ * Create a mbuf for the gather list and push it onto the host stack.
+ * BPF tap and stats are on us.
+ */
+ if (fDst & INTNETTRUNKDIR_HOST)
+ {
+ mbuf_t pMBuf = vboxNetFltDarwinMBufFromSG(pThis, pSG);
+ if (pMBuf)
+ {
+ void *pvEthHdr = mbuf_data(pMBuf);
+ unsigned const cbEthHdr = 14;
+ struct ifnet_stat_increment_param stats;
+
+ RT_ZERO(stats);
+ stats.packets_in = 1;
+ stats.bytes_in = mbuf_len(pMBuf); /* full ethernet frame */
+
+ mbuf_pkthdr_setrcvif(pMBuf, pIfNet);
+ mbuf_pkthdr_setheader(pMBuf, pvEthHdr); /* link-layer header */
+ mbuf_adj(pMBuf, cbEthHdr); /* move to payload */
+
+#if 0 /* XXX: disabled since we don't request promiscuous from intnet */
+ /*
+ * TODO: Since intnet knows whether it forwarded us
+ * this packet because it's for us or because we are
+ * promiscuous, it can perhaps set a flag for us in
+ * INTNETSG::fFlags so that we don't have to re-check
+ * it here.
+ */
+ PCRTNETETHERHDR pcEthHdr = (PCRTNETETHERHDR)pvEthHdr;
+ if ( (pcEthHdr->DstMac.au8[0] & 1) == 0 /* unicast? */
+ && memcmp(&pcEthHdr->DstMac, &pThis->u.s.MacAddr, sizeof(RTMAC)) != 0)
+ {
+ mbuf_setflags_mask(pMBuf, MBUF_PROMISC, MBUF_PROMISC);
+ }
+#endif
+
+ bpf_tap_in(pIfNet, DLT_EN10MB, pMBuf, pvEthHdr, cbEthHdr);
+ errno_t err = ifnet_input(pIfNet, pMBuf, &stats);
+ if (err)
+ rc = RTErrConvertFromErrno(err);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+
+ vboxNetFltDarwinReleaseIfNet(pThis, pIfNet);
+ }
+
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return rc;
+}
+
+
+void vboxNetFltPortOsSetActive(PVBOXNETFLTINS pThis, bool fActive)
+{
+ IPRT_DARWIN_SAVE_EFL_AC();
+ ifnet_t pIfNet = vboxNetFltDarwinRetainIfNet(pThis);
+ if (pIfNet)
+ {
+ if (pThis->fDisablePromiscuous)
+ {
+ /*
+ * Promiscuous mode should not be used (wireless), we just need to
+ * make sure the interface is up.
+ */
+ if (fActive)
+ {
+ u_int16_t fIf = ifnet_flags(pIfNet);
+ if ((fIf & (IFF_UP | IFF_RUNNING)) != (IFF_UP | IFF_RUNNING))
+ {
+ ifnet_set_flags(pIfNet, IFF_UP, IFF_UP);
+ ifnet_ioctl(pIfNet, 0, SIOCSIFFLAGS, NULL);
+ }
+ }
+ }
+ else
+ {
+ /*
+ * This api is a bit weird, the best reference is the code.
+ *
+ * Also, we have a bit or race conditions wrt the maintenance of
+ * host the interface promiscuity for vboxNetFltPortOsIsPromiscuous.
+ */
+ unsigned const cPromiscBefore = VBOX_GET_PCOUNT(pIfNet);
+ u_int16_t fIf;
+ if (fActive)
+ {
+ Assert(!pThis->u.s.fSetPromiscuous);
+ errno_t err = ENETDOWN;
+ ASMAtomicWriteBool(&pThis->u.s.fNeedSetPromiscuous, true);
+
+ /*
+ * Try bring the interface up and running if it's down.
+ */
+ fIf = ifnet_flags(pIfNet);
+ if ((fIf & (IFF_UP | IFF_RUNNING)) != (IFF_UP | IFF_RUNNING))
+ {
+ err = ifnet_set_flags(pIfNet, IFF_UP, IFF_UP);
+ errno_t err2 = ifnet_ioctl(pIfNet, 0, SIOCSIFFLAGS, NULL);
+ if (!err)
+ err = err2;
+ fIf = ifnet_flags(pIfNet);
+ }
+
+ /*
+ * Is it already up? If it isn't, leave it to the link event or
+ * we'll upset if_pcount (as stated above, ifnet_set_promiscuous is weird).
+ */
+ if ((fIf & (IFF_UP | IFF_RUNNING)) == (IFF_UP | IFF_RUNNING))
+ {
+ err = ifnet_set_promiscuous(pIfNet, 1);
+ pThis->u.s.fSetPromiscuous = err == 0;
+ if (!err)
+ {
+ ASMAtomicWriteBool(&pThis->u.s.fNeedSetPromiscuous, false);
+
+ /* check if it actually worked, this stuff is not always behaving well. */
+ if (!(ifnet_flags(pIfNet) & IFF_PROMISC))
+ {
+ err = ifnet_set_flags(pIfNet, IFF_PROMISC, IFF_PROMISC);
+ if (!err)
+ err = ifnet_ioctl(pIfNet, 0, SIOCSIFFLAGS, NULL);
+ if (!err)
+ Log(("vboxNetFlt: fixed IFF_PROMISC on %s (%d->%d)\n", pThis->szName, cPromiscBefore, VBOX_GET_PCOUNT(pIfNet)));
+ else
+ Log(("VBoxNetFlt: failed to fix IFF_PROMISC on %s, err=%d (%d->%d)\n",
+ pThis->szName, err, cPromiscBefore, VBOX_GET_PCOUNT(pIfNet)));
+ }
+ }
+ else
+ Log(("VBoxNetFlt: ifnet_set_promiscuous -> err=%d grr! (%d->%d)\n", err, cPromiscBefore, VBOX_GET_PCOUNT(pIfNet)));
+ }
+ else if (!err)
+ Log(("VBoxNetFlt: Waiting for the link to come up... (%d->%d)\n", cPromiscBefore, VBOX_GET_PCOUNT(pIfNet)));
+ if (err)
+ LogRel(("VBoxNetFlt: Failed to put '%s' into promiscuous mode, err=%d (%d->%d)\n", pThis->szName, err, cPromiscBefore, VBOX_GET_PCOUNT(pIfNet)));
+ }
+ else
+ {
+ ASMAtomicWriteBool(&pThis->u.s.fNeedSetPromiscuous, false);
+ if (pThis->u.s.fSetPromiscuous)
+ {
+ errno_t err = ifnet_set_promiscuous(pIfNet, 0);
+ AssertMsg(!err, ("%d\n", err)); NOREF(err);
+ }
+ pThis->u.s.fSetPromiscuous = false;
+
+ fIf = ifnet_flags(pIfNet);
+ Log(("VBoxNetFlt: fIf=%#x; %d->%d\n", fIf, cPromiscBefore, VBOX_GET_PCOUNT(pIfNet)));
+ }
+ }
+
+ vboxNetFltDarwinReleaseIfNet(pThis, pIfNet);
+ }
+ IPRT_DARWIN_RESTORE_EFL_AC();
+}
+
+
+int vboxNetFltOsDisconnectIt(PVBOXNETFLTINS pThis)
+{
+ /* Nothing to do here. */
+ RT_NOREF(pThis);
+ return VINF_SUCCESS;
+}
+
+
+int vboxNetFltOsConnectIt(PVBOXNETFLTINS pThis)
+{
+ /* Nothing to do here. */
+ RT_NOREF(pThis);
+ return VINF_SUCCESS;
+}
+
+
+void vboxNetFltOsDeleteInstance(PVBOXNETFLTINS pThis)
+{
+ IPRT_DARWIN_SAVE_EFL_AC();
+
+ /*
+ * Carefully obtain the interface filter reference and detach it.
+ */
+ RTSpinlockAcquire(pThis->hSpinlock);
+ interface_filter_t pIfFilter = ASMAtomicUoReadPtrT(&pThis->u.s.pIfFilter, interface_filter_t);
+ if (pIfFilter)
+ ASMAtomicUoWriteNullPtr(&pThis->u.s.pIfFilter);
+ RTSpinlockRelease(pThis->hSpinlock);
+
+ if (pIfFilter)
+ iflt_detach(pIfFilter);
+
+ if (pThis->u.s.pSysSock != NULL)
+ {
+ RT_GCC_NO_WARN_DEPRECATED_BEGIN
+
+ sock_close(pThis->u.s.pSysSock);
+ pThis->u.s.pSysSock = NULL;
+
+ RT_GCC_NO_WARN_DEPRECATED_END
+ }
+
+ IPRT_DARWIN_RESTORE_EFL_AC();
+}
+
+
+int vboxNetFltOsInitInstance(PVBOXNETFLTINS pThis, void *pvContext)
+{
+ NOREF(pvContext);
+
+ int rc = vboxNetFltDarwinAttachToInterface(pThis, false /* fRediscovery */);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ if (pThis->pSwitchPort->pfnNotifyHostAddress == NULL)
+ return rc;
+
+ /*
+ * XXX: uwe
+ *
+ * Learn host's IP addresses and set up notifications for changes.
+ * To avoid racing, set up notifications first.
+ *
+ * XXX: This should probably be global, since the only thing
+ * specific to ifnet here is its IPv6 link-local address.
+ */
+ IPRT_DARWIN_SAVE_EFL_AC();
+ errno_t error;
+
+ /** @todo Figure out how to replace the socket stuff we use to detect
+ * addresses here as 10.5 deprecates it. */
+ RT_GCC_NO_WARN_DEPRECATED_BEGIN
+
+ /** @todo reorg code to not have numerous returns with duplicate code... */
+ /** @todo reorg code to not have numerous returns with duplicate code... */
+ /** @todo reorg code to not have numerous returns with duplicate code... */
+ /** @todo reorg code to not have numerous returns with duplicate code... */
+ /** @todo reorg code to not have numerous returns with duplicate code... */
+ /** @todo reorg code to not have numerous returns with duplicate code... */
+ /** @todo reorg code to not have numerous returns with duplicate code... */
+ /** @todo reorg code to not have numerous returns with duplicate code... */
+ /** @todo reorg code to not have numerous returns with duplicate code... */
+
+ error = sock_socket(PF_SYSTEM, SOCK_RAW, SYSPROTO_EVENT,
+ vboxNetFltDarwinSysSockUpcall, pThis,
+ &pThis->u.s.pSysSock);
+ if (error != 0)
+ {
+ LogRel(("sock_socket(SYSPROTO_EVENT): error %d\n", error));
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return rc;
+ }
+
+ int nbio = 1;
+ error = sock_ioctl(pThis->u.s.pSysSock, FIONBIO, &nbio);
+ if (error != 0)
+ {
+ LogRel(("FIONBIO: error %d\n", error));
+ sock_close(pThis->u.s.pSysSock);
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return rc;
+ }
+
+ if (!sock_isnonblocking(pThis->u.s.pSysSock))
+ {
+ LogRel(("FIONBIO ok, but socket is blocking?!\n"));
+ sock_close(pThis->u.s.pSysSock);
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return rc;
+ }
+
+ struct kev_request req;
+ req.vendor_code = KEV_VENDOR_APPLE;
+ req.kev_class = KEV_NETWORK_CLASS;
+ req.kev_subclass = KEV_ANY_SUBCLASS; /* need both INET and INET6, so have to request all */
+
+ error = sock_ioctl(pThis->u.s.pSysSock, SIOCSKEVFILT, &req);
+ if (error != 0)
+ {
+ LogRel(("SIOCSKEVFILT: error %d\n", error));
+ sock_close(pThis->u.s.pSysSock);
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return rc;
+ }
+ RT_GCC_NO_WARN_DEPRECATED_END
+
+ ifnet_t pIfNet = pThis->u.s.pIfNet; /* already retained */
+
+ ifaddr_t *pIfAddrList;
+ error = ifnet_get_address_list(/* all interfaces*/ NULL, &pIfAddrList);
+ if (error != 0)
+ {
+ LogRel(("ifnet_get_address_list: error %d\n", error));
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return rc;
+ }
+
+ for (ifaddr_t *pIfAddr = pIfAddrList; *pIfAddr != NULL; ++pIfAddr)
+ {
+ ifaddr_t ifa = *pIfAddr;
+ sa_family_t family = ifaddr_address_family(ifa);
+ struct sockaddr_storage ss;
+
+ error = ifaddr_address(ifa, (struct sockaddr *)&ss, sizeof(ss));
+ if (error != 0)
+ {
+ LogRel(("getting address family %d: error %d\n", family, error));
+ continue;
+ }
+
+ if (family == AF_INET)
+ {
+ struct sockaddr_in *sin = (struct sockaddr_in *)&ss;
+ u_int32_t u32Addr = ntohl(sin->sin_addr.s_addr);
+
+ if (VBOX_IN_LOOPBACK(u32Addr))
+ continue;
+
+ if (ifaddr_ifnet(ifa) != pIfNet && VBOX_IN_LINKLOCAL(u32Addr))
+ continue;
+
+ Log(("> inet %RTnaipv4\n", sin->sin_addr.s_addr));
+ pThis->pSwitchPort->pfnNotifyHostAddress(pThis->pSwitchPort,
+ /* :fAdded */ true, kIntNetAddrType_IPv4, &sin->sin_addr);
+ }
+ else if (family == AF_INET6)
+ {
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&ss;
+
+ if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr))
+ continue;
+
+ /* link-local from other interfaces are out of scope */
+ if (ifaddr_ifnet(ifa) != pIfNet && IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))
+ continue;
+
+ Log(("> inet6 %RTnaipv6\n", &sin6->sin6_addr));
+ pThis->pSwitchPort->pfnNotifyHostAddress(pThis->pSwitchPort,
+ /* :fAdded */ true, kIntNetAddrType_IPv6, &sin6->sin6_addr);
+ }
+ }
+
+ ifnet_free_address_list(pIfAddrList);
+
+ /*
+ * Now that we've got current addresses, check for events that
+ * might have happened while we were working.
+ */
+ vboxNetFltDarwinSysSockUpcall(pThis->u.s.pSysSock, pThis, MBUF_DONTWAIT);
+
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return rc;
+}
+
+
+static void vboxNetFltDarwinSysSockUpcall(socket_t pSysSock, void *pvData, int fWait)
+{
+ PVBOXNETFLTINS pThis = (PVBOXNETFLTINS)pvData;
+ errno_t error;
+
+ NOREF(fWait);
+
+ if (RT_UNLIKELY(pSysSock != pThis->u.s.pSysSock))
+ {
+ Log(("vboxNetFltDarwinSysSockUpcall: %p != %p?\n", pSysSock, pThis->u.s.pSysSock));
+ return;
+ }
+
+ ifnet_t pIfNet = pThis->u.s.pIfNet; /* XXX: retain? */
+ ifnet_family_t if_family = ifnet_family(pIfNet);
+ u_int32_t if_unit = ifnet_unit(pIfNet);
+
+ for (;;)
+ {
+ mbuf_t m;
+ size_t len = sizeof(struct kern_event_msg) - sizeof(u_int32_t) + sizeof(struct kev_in6_data);
+
+ RT_GCC_NO_WARN_DEPRECATED_BEGIN
+ error = sock_receivembuf(pSysSock, NULL, &m, 0, &len);
+ RT_GCC_NO_WARN_DEPRECATED_END
+ if (error != 0)
+ {
+ if (error == EWOULDBLOCK)
+ {
+ Log(("vboxNetFltDarwinSysSockUpcall: EWOULDBLOCK - we are done\n"));
+ error = 0;
+ }
+ else
+ Log(("sock_receivembuf: error %d\n", error));
+ break;
+ }
+
+ if (len < sizeof(struct kern_event_msg) - sizeof(u_int32_t))
+ {
+ Log(("vboxNetFltDarwinSysSockUpcall: %u bytes is too short\n", (unsigned int)len));
+ mbuf_freem(m);
+ return;
+ }
+
+ struct kern_event_msg *msg = (struct kern_event_msg *)mbuf_data(m);
+ if (msg->kev_subclass == KEV_INET_SUBCLASS)
+ {
+ if (len - (sizeof(struct kern_event_msg) - sizeof(u_int32_t)) < sizeof(struct kev_in_data))
+ {
+ Log(("vboxNetFltDarwinSysSockUpcall: %u bytes is too short for KEV_INET_SUBCLASS\n", (unsigned int)len));
+ mbuf_freem(m);
+ return;
+ }
+
+ struct kev_in_data *iev = (struct kev_in_data *)msg->event_data;
+ struct net_event_data *link = &iev->link_data;
+ PCRTNETADDRU pAddr = (PCRTNETADDRU)&iev->ia_addr;
+ u_int32_t u32Addr = ntohl(pAddr->IPv4.u);
+
+ if (VBOX_IN_LOOPBACK(u32Addr))
+ {
+ mbuf_freem(m);
+ continue;
+ }
+
+ if ( (link->if_family != if_family || link->if_unit != if_unit)
+ && VBOX_IN_LINKLOCAL(u32Addr))
+ {
+ mbuf_freem(m);
+ continue;
+ }
+
+ switch (msg->event_code)
+ {
+ case KEV_INET_NEW_ADDR:
+ Log(("KEV_INET_NEW_ADDR %.*s%d: %RTnaipv4\n", IFNAMSIZ, link->if_name, link->if_unit, pAddr->IPv4.u));
+ pThis->pSwitchPort->pfnNotifyHostAddress(pThis->pSwitchPort, true /*fAdded*/, kIntNetAddrType_IPv4, pAddr);
+ break;
+
+ case KEV_INET_ADDR_DELETED:
+ Log(("KEV_INET_ADDR_DELETED %.*s%d: %RTnaipv4\n", IFNAMSIZ, link->if_name, link->if_unit, pAddr->IPv4.u));
+ pThis->pSwitchPort->pfnNotifyHostAddress(pThis->pSwitchPort, false /*fAdded*/, kIntNetAddrType_IPv4, pAddr);
+ break;
+
+ default:
+ Log(("KEV INET event %u %.*s%d: addr %RTnaipv4\n",
+ msg->event_code, IFNAMSIZ, link->if_name, link->if_unit, pAddr->IPv4.u));
+ break;
+ }
+ }
+ else if (msg->kev_subclass == KEV_INET6_SUBCLASS)
+ {
+ if (len - (sizeof(struct kern_event_msg) - sizeof(u_int32_t)) < sizeof(struct kev_in6_data))
+ {
+ Log(("vboxNetFltDarwinSysSockUpcall: %u bytes is too short for KEV_INET6_SUBCLASS\n",
+ (unsigned int)len));
+ mbuf_freem(m);
+ return;
+ }
+
+ struct kev_in6_data *iev6 = (struct kev_in6_data *)msg->event_data;
+ struct net_event_data *link = &iev6->link_data;
+ PCRTNETADDRU pAddr = (PCRTNETADDRU)&iev6->ia_addr.sin6_addr;
+
+ if (IN6_IS_ADDR_LOOPBACK(&iev6->ia_addr.sin6_addr))
+ {
+ mbuf_freem(m);
+ continue;
+ }
+
+ if ( (link->if_family != if_family || link->if_unit != if_unit)
+ && IN6_IS_ADDR_LINKLOCAL(&iev6->ia_addr.sin6_addr))
+ {
+ mbuf_freem(m);
+ continue;
+ }
+
+ switch (msg->event_code)
+ {
+ case KEV_INET6_NEW_USER_ADDR:
+ Log(("KEV_INET6_NEW_USER_ADDR %.*s%d: %RTnaipv6\n",
+ IFNAMSIZ, link->if_name, link->if_unit, pAddr));
+ goto kev_inet6_new;
+
+ case KEV_INET6_NEW_LL_ADDR:
+ Log(("KEV_INET6_NEW_LL_ADDR %.*s%d: %RTnaipv6\n",
+ IFNAMSIZ, link->if_name, link->if_unit, pAddr));
+ goto kev_inet6_new;
+
+ case KEV_INET6_NEW_RTADV_ADDR:
+ Log(("KEV_INET6_NEW_RTADV_ADDR %.*s%d: %RTnaipv6\n",
+ IFNAMSIZ, link->if_name, link->if_unit, pAddr));
+ goto kev_inet6_new;
+
+ kev_inet6_new:
+ pThis->pSwitchPort->pfnNotifyHostAddress(pThis->pSwitchPort, true /*fAdded*/, kIntNetAddrType_IPv6, pAddr);
+ break;
+
+ case KEV_INET6_ADDR_DELETED:
+ Log(("KEV_INET6_ADDR_DELETED %.*s%d: %RTnaipv6\n",
+ IFNAMSIZ, link->if_name, link->if_unit, pAddr));
+
+ pThis->pSwitchPort->pfnNotifyHostAddress(pThis->pSwitchPort, false /*fAdded*/, kIntNetAddrType_IPv6, pAddr);
+ break;
+
+ default:
+ Log(("KEV INET6 event %u %.*s%d: addr %RTnaipv6\n",
+ msg->event_code, IFNAMSIZ, link->if_name, link->if_unit, pAddr));
+ break;
+ }
+ }
+ else
+ Log(("vboxNetFltDarwinSysSockUpcall: subclass %u ignored\n", (unsigned)msg->kev_subclass));
+
+ mbuf_freem(m);
+ }
+}
+
+
+int vboxNetFltOsPreInitInstance(PVBOXNETFLTINS pThis)
+{
+ /*
+ * Init the darwin specific members.
+ */
+ pThis->u.s.pIfNet = NULL;
+ pThis->u.s.pIfFilter = NULL;
+ pThis->u.s.fSetPromiscuous = false;
+ pThis->u.s.fNeedSetPromiscuous = false;
+ //pThis->u.s.MacAddr = {0};
+ pThis->u.s.pSysSock = NULL;
+
+ return VINF_SUCCESS;
+}
+
+
+void vboxNetFltPortOsNotifyMacAddress(PVBOXNETFLTINS pThis, void *pvIfData, PCRTMAC pMac)
+{
+ NOREF(pThis); NOREF(pvIfData); NOREF(pMac);
+}
+
+
+int vboxNetFltPortOsConnectInterface(PVBOXNETFLTINS pThis, void *pvIf, void **ppvIfData)
+{
+ /* Nothing to do */
+ NOREF(pThis); NOREF(pvIf); NOREF(ppvIfData);
+ return VINF_SUCCESS;
+}
+
+
+int vboxNetFltPortOsDisconnectInterface(PVBOXNETFLTINS pThis, void *pvIfData)
+{
+ /* Nothing to do */
+ NOREF(pThis); NOREF(pvIfData);
+ return VINF_SUCCESS;
+}
+
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/darwin/loadnetflt.sh b/src/VBox/HostDrivers/VBoxNetFlt/darwin/loadnetflt.sh
new file mode 100755
index 00000000..0fede55c
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/darwin/loadnetflt.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 <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+SCRIPT_NAME="loadnetflt"
+XNU_VERSION=`LC_ALL=C uname -r | LC_ALL=C cut -d . -f 1`
+
+DRVNAME="VBoxNetFlt.kext"
+BUNDLE="org.virtualbox.kext.VBoxNetFlt"
+
+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/VBoxNetFlt/freebsd/Makefile b/src/VBox/HostDrivers/VBoxNetFlt/freebsd/Makefile
new file mode 100644
index 00000000..596f80a0
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/freebsd/Makefile
@@ -0,0 +1,57 @@
+# $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 <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+KMOD = vboxnetflt
+
+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 = \
+ VBoxNetFlt-freebsd.c \
+ VBoxNetFlt.c \
+ SUPR0IdcClient-freebsd.c \
+ SUPR0IdcClient.c \
+ SUPR0IdcClientComponent.c
+
+SRCS += device_if.h bus_if.h opt_netgraph.h
+
+.include <bsd.kmod.mk>
+
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/freebsd/VBoxNetFlt-freebsd.c b/src/VBox/HostDrivers/VBoxNetFlt/freebsd/VBoxNetFlt-freebsd.c
new file mode 100644
index 00000000..b8b4212e
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/freebsd/VBoxNetFlt-freebsd.c
@@ -0,0 +1,817 @@
+/* $Id: VBoxNetFlt-freebsd.c $ */
+/** @file
+ * VBoxNetFlt - Network Filter Driver (Host), FreeBSD Specific Code.
+ */
+
+/*
+ * Copyright (c) 2009 Fredrik Lindberg <fli@shapeshifter.se>
+ *
+ * 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 <sys/param.h>
+#undef PVM
+#include <sys/types.h>
+#include <sys/module.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/kernel.h>
+#include <sys/fcntl.h>
+#include <sys/conf.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/syscallsubr.h>
+#include <sys/queue.h>
+#include <sys/taskqueue.h>
+
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/if_dl.h>
+#include <net/if_types.h>
+#include <net/ethernet.h>
+
+#include <netgraph/ng_message.h>
+#include <netgraph/netgraph.h>
+#include <netgraph/ng_parse.h>
+
+#define LOG_GROUP LOG_GROUP_NET_FLT_DRV
+#include <VBox/version.h>
+#include <VBox/err.h>
+#include <VBox/log.h>
+#include <VBox/intnetinline.h>
+#include <iprt/initterm.h>
+#include <iprt/string.h>
+#include <iprt/spinlock.h>
+#include <iprt/process.h>
+#include <iprt/assert.h>
+#include <iprt/uuid.h>
+#include <iprt/alloc.h>
+#include <iprt/err.h>
+
+#define VBOXNETFLT_OS_SPECFIC 1
+#include "../VBoxNetFltInternal.h"
+
+static int vboxnetflt_modevent(struct module *, int, void *);
+static ng_constructor_t ng_vboxnetflt_constructor;
+static ng_rcvmsg_t ng_vboxnetflt_rcvmsg;
+static ng_shutdown_t ng_vboxnetflt_shutdown;
+static ng_newhook_t ng_vboxnetflt_newhook;
+static ng_rcvdata_t ng_vboxnetflt_rcvdata;
+static ng_disconnect_t ng_vboxnetflt_disconnect;
+static int ng_vboxnetflt_mod_event(module_t mod, int event, void *data);
+
+/** Netgraph node type */
+#define NG_VBOXNETFLT_NODE_TYPE "vboxnetflt"
+/** Netgraph message cookie */
+#define NGM_VBOXNETFLT_COOKIE 0x56424f58
+
+/** Input netgraph hook name */
+#define NG_VBOXNETFLT_HOOK_IN "input"
+/** Output netgraph hook name */
+#define NG_VBOXNETFLT_HOOK_OUT "output"
+
+/** mbuf tag identifier */
+#define MTAG_VBOX 0x56424f58
+/** mbuf packet tag */
+#define PACKET_TAG_VBOX 128
+
+#if defined(__FreeBSD_version) && __FreeBSD_version >= 800500
+# include <sys/jail.h>
+# include <net/vnet.h>
+
+# 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 */
+
+/*
+ * Netgraph command list, we don't support any
+ * additional commands.
+ */
+static const struct ng_cmdlist ng_vboxnetflt_cmdlist[] =
+{
+ { 0 }
+};
+
+/*
+ * Netgraph type definition
+ */
+static struct ng_type ng_vboxnetflt_typestruct =
+{
+ .version = NG_ABI_VERSION,
+ .name = NG_VBOXNETFLT_NODE_TYPE,
+ .mod_event = vboxnetflt_modevent,
+ .constructor= ng_vboxnetflt_constructor,
+ .rcvmsg = ng_vboxnetflt_rcvmsg,
+ .shutdown = ng_vboxnetflt_shutdown,
+ .newhook = ng_vboxnetflt_newhook,
+ .rcvdata = ng_vboxnetflt_rcvdata,
+ .disconnect = ng_vboxnetflt_disconnect,
+ .cmdlist = ng_vboxnetflt_cmdlist,
+};
+NETGRAPH_INIT(vboxnetflt, &ng_vboxnetflt_typestruct);
+
+/*
+ * Use vboxnetflt because the kernel module is named vboxnetflt and vboxnetadp
+ * depends on this when loading dependencies.
+ * NETGRAP_INIT will prefix the given name with ng_ so MODULE_DEPEND needs the
+ * prefixed name.
+ */
+MODULE_VERSION(vboxnetflt, 1);
+MODULE_DEPEND(ng_vboxnetflt, vboxdrv, 1, 1, 1);
+
+/**
+ * The (common) global data.
+ */
+static VBOXNETFLTGLOBALS g_VBoxNetFltGlobals;
+
+/**
+ * Module event handler, called from netgraph subsystem.
+ */
+static int vboxnetflt_modevent(struct module *pMod, int enmEventType, void *pvArg)
+{
+ int rc;
+
+ Log(("VBoxNetFltFreeBSDModuleEvent\n"));
+
+ switch (enmEventType)
+ {
+ case MOD_LOAD:
+ rc = RTR0Init(0);
+ if (RT_FAILURE(rc))
+ {
+ printf("RTR0Init failed %d\n", rc);
+ return RTErrConvertToErrno(rc);
+ }
+
+ memset(&g_VBoxNetFltGlobals, 0, sizeof(VBOXNETFLTGLOBALS));
+ rc = vboxNetFltInitGlobalsAndIdc(&g_VBoxNetFltGlobals);
+ if (RT_FAILURE(rc))
+ {
+ printf("vboxNetFltInitGlobalsAndIdc failed %d\n", rc);
+ return RTErrConvertToErrno(rc);
+ }
+ /* No MODULE_VERSION in ng_ether so we can't MODULE_DEPEND it */
+ kern_kldload(curthread, "ng_ether", NULL);
+ break;
+
+ case MOD_UNLOAD:
+ rc = vboxNetFltTryDeleteIdcAndGlobals(&g_VBoxNetFltGlobals);
+ memset(&g_VBoxNetFltGlobals, 0, sizeof(VBOXNETFLTGLOBALS));
+ RTR0Term();
+ break;
+
+ case MOD_SHUTDOWN:
+ case MOD_QUIESCE:
+ default:
+ return EOPNOTSUPP;
+ }
+
+ if (RT_SUCCESS(rc))
+ return 0;
+ return RTErrConvertToErrno(rc);
+}
+
+/*
+ * Convert from mbufs to vbox scatter-gather data structure
+ */
+static void vboxNetFltFreeBSDMBufToSG(PVBOXNETFLTINS pThis, struct mbuf *m, PINTNETSG pSG,
+ unsigned int cSegs, unsigned int segOffset)
+{
+ static uint8_t const s_abZero[128] = {0};
+ unsigned int i;
+ struct mbuf *m0;
+
+ IntNetSgInitTempSegs(pSG, m_length(m, NULL), cSegs, 0 /*cSegsUsed*/);
+
+ for (m0 = m, i = segOffset; m0; m0 = m0->m_next)
+ {
+ if (m0->m_len == 0)
+ continue;
+
+ pSG->aSegs[i].cb = m0->m_len;
+ pSG->aSegs[i].pv = mtod(m0, uint8_t *);
+ pSG->aSegs[i].Phys = NIL_RTHCPHYS;
+ i++;
+ }
+
+#ifdef PADD_RUNT_FRAMES_FROM_HOST
+ if (pSG->cbTotal < 60)
+ {
+ pSG->aSegs[i].Phys = NIL_RTHCPHYS;
+ pSG->aSegs[i].pv = (void *)&s_abZero[0];
+ pSG->aSegs[i].cb = 60 - pSG->cbTotal;
+ pSG->cbTotal = 60;
+ i++;
+ }
+#endif
+
+ pSG->cSegsUsed = i;
+}
+
+/*
+ * Convert to mbufs from vbox scatter-gather data structure
+ */
+static struct mbuf * vboxNetFltFreeBSDSGMBufFromSG(PVBOXNETFLTINS pThis, PINTNETSG pSG)
+{
+ struct mbuf *m;
+ int error;
+ unsigned int i;
+
+ if (pSG->cbTotal == 0)
+ return (NULL);
+
+ m = m_getcl(M_WAITOK, MT_DATA, M_PKTHDR);
+ if (m == NULL)
+ return (NULL);
+
+ m->m_pkthdr.len = m->m_len = 0;
+ m->m_pkthdr.rcvif = NULL;
+
+ for (i = 0; i < pSG->cSegsUsed; i++)
+ {
+ error = m_append(m, pSG->aSegs[i].cb, pSG->aSegs[i].pv);
+ if (error == 0)
+ {
+ m_freem(m);
+ return (NULL);
+ }
+ }
+ return (m);
+}
+
+
+static int ng_vboxnetflt_constructor(node_p node)
+{
+ /* Nothing to do */
+ return (EINVAL);
+}
+
+/*
+ * Setup netgraph hooks
+ */
+static int ng_vboxnetflt_newhook(node_p node, hook_p hook, const char *name)
+{
+ PVBOXNETFLTINS pThis = NG_NODE_PRIVATE(node);
+
+ if (strcmp(name, NG_VBOXNETFLT_HOOK_IN) == 0)
+ {
+#if __FreeBSD_version >= 800000
+ NG_HOOK_SET_TO_INBOUND(hook);
+#endif
+ pThis->u.s.input = hook;
+ }
+ else if (strcmp(name, NG_VBOXNETFLT_HOOK_OUT) == 0)
+ {
+ pThis->u.s.output = hook;
+ }
+ else
+ return (EINVAL);
+
+ NG_HOOK_HI_STACK(hook);
+ return (0);
+}
+
+/**
+ * Netgraph message processing for node specific messages.
+ * We don't accept any special messages so this is not used.
+ */
+static int ng_vboxnetflt_rcvmsg(node_p node, item_p item, hook_p lasthook)
+{
+ PVBOXNETFLTINS pThis = NG_NODE_PRIVATE(node);
+ struct ng_mesg *msg;
+ int error = 0;
+
+ NGI_GET_MSG(item, msg);
+ if (msg->header.typecookie != NGM_VBOXNETFLT_COOKIE)
+ return (EINVAL);
+
+ switch (msg->header.cmd)
+ {
+ default:
+ error = EINVAL;
+ }
+ return (error);
+}
+
+/**
+ * Handle data on netgraph hooks.
+ * Frames processing is deferred to a taskqueue because this might
+ * be called with non-sleepable locks held and code paths inside
+ * the virtual switch might sleep.
+ */
+static int ng_vboxnetflt_rcvdata(hook_p hook, item_p item)
+{
+ const node_p node = NG_HOOK_NODE(hook);
+ PVBOXNETFLTINS pThis = NG_NODE_PRIVATE(node);
+ struct ifnet *ifp = pThis->u.s.ifp;
+ struct mbuf *m;
+ struct m_tag *mtag;
+ bool fActive;
+
+ VBOXCURVNET_SET(ifp->if_vnet);
+ fActive = vboxNetFltTryRetainBusyActive(pThis);
+
+ NGI_GET_M(item, m);
+ NG_FREE_ITEM(item);
+
+ /* Locate tag to see if processing should be skipped for this frame */
+ mtag = m_tag_locate(m, MTAG_VBOX, PACKET_TAG_VBOX, NULL);
+ if (mtag != NULL)
+ {
+ m_tag_unlink(m, mtag);
+ m_tag_free(mtag);
+ }
+
+ /*
+ * Handle incoming hook. This is connected to the
+ * input path of the interface, thus handling incoming frames.
+ */
+ if (pThis->u.s.input == hook)
+ {
+ if (mtag != NULL || !fActive)
+ {
+ ether_demux(ifp, m);
+ if (fActive)
+ vboxNetFltRelease(pThis, true /*fBusy*/);
+ VBOXCURVNET_RESTORE();
+ return (0);
+ }
+ mtx_lock_spin(&pThis->u.s.inq.ifq_mtx);
+ _IF_ENQUEUE(&pThis->u.s.inq, m);
+ mtx_unlock_spin(&pThis->u.s.inq.ifq_mtx);
+#if __FreeBSD_version > 1100100
+ taskqueue_enqueue(taskqueue_fast, &pThis->u.s.tskin);
+#else
+ taskqueue_enqueue_fast(taskqueue_fast, &pThis->u.s.tskin);
+#endif
+ }
+ /*
+ * Handle mbufs on the outgoing hook, frames going to the interface
+ */
+ else if (pThis->u.s.output == hook)
+ {
+ if (mtag != NULL || !fActive)
+ {
+ int rc = ether_output_frame(ifp, m);
+ if (fActive)
+ vboxNetFltRelease(pThis, true /*fBusy*/);
+ VBOXCURVNET_RESTORE();
+ return rc;
+ }
+ mtx_lock_spin(&pThis->u.s.outq.ifq_mtx);
+ _IF_ENQUEUE(&pThis->u.s.outq, m);
+ mtx_unlock_spin(&pThis->u.s.outq.ifq_mtx);
+#if __FreeBSD_version > 1100100
+ taskqueue_enqueue(taskqueue_fast, &pThis->u.s.tskout);
+#else
+ taskqueue_enqueue_fast(taskqueue_fast, &pThis->u.s.tskout);
+#endif
+ }
+ else
+ {
+ m_freem(m);
+ }
+
+ if (fActive)
+ vboxNetFltRelease(pThis, true /*fBusy*/);
+ VBOXCURVNET_RESTORE();
+ return (0);
+}
+
+static int ng_vboxnetflt_shutdown(node_p node)
+{
+ PVBOXNETFLTINS pThis = NG_NODE_PRIVATE(node);
+ bool fActive;
+
+ /* Prevent node shutdown if we're active */
+ if (pThis->enmTrunkState == INTNETTRUNKIFSTATE_ACTIVE)
+ return (EBUSY);
+ NG_NODE_UNREF(node);
+ return (0);
+}
+
+static int ng_vboxnetflt_disconnect(hook_p hook)
+{
+ return (0);
+}
+
+/**
+ * Input processing task, handles incoming frames
+ */
+static void vboxNetFltFreeBSDinput(void *arg, int pending)
+{
+ PVBOXNETFLTINS pThis = (PVBOXNETFLTINS)arg;
+ struct mbuf *m, *m0;
+ struct ifnet *ifp = pThis->u.s.ifp;
+ unsigned int cSegs = 0;
+ bool fDropIt = false, fActive;
+ PINTNETSG pSG;
+
+ VBOXCURVNET_SET(ifp->if_vnet);
+ vboxNetFltRetain(pThis, true /* fBusy */);
+ for (;;)
+ {
+ mtx_lock_spin(&pThis->u.s.inq.ifq_mtx);
+ _IF_DEQUEUE(&pThis->u.s.inq, m);
+ mtx_unlock_spin(&pThis->u.s.inq.ifq_mtx);
+ if (m == NULL)
+ break;
+
+ for (m0 = m; m0 != NULL; m0 = m0->m_next)
+ if (m0->m_len > 0)
+ cSegs++;
+
+#ifdef PADD_RUNT_FRAMES_FROM_HOST
+ if (m_length(m, NULL) < 60)
+ cSegs++;
+#endif
+
+ /* Create a copy and deliver to the virtual switch */
+ pSG = RTMemTmpAlloc(RT_UOFFSETOF_DYN(INTNETSG, aSegs[cSegs]));
+ vboxNetFltFreeBSDMBufToSG(pThis, m, pSG, cSegs, 0);
+ fDropIt = pThis->pSwitchPort->pfnRecv(pThis->pSwitchPort, NULL /* pvIf */, pSG, INTNETTRUNKDIR_WIRE);
+ RTMemTmpFree(pSG);
+ if (fDropIt)
+ m_freem(m);
+ else
+ ether_demux(ifp, m);
+ }
+ vboxNetFltRelease(pThis, true /* fBusy */);
+ VBOXCURVNET_RESTORE();
+}
+
+/**
+ * Output processing task, handles outgoing frames
+ */
+static void vboxNetFltFreeBSDoutput(void *arg, int pending)
+{
+ PVBOXNETFLTINS pThis = (PVBOXNETFLTINS)arg;
+ struct mbuf *m, *m0;
+ struct ifnet *ifp = pThis->u.s.ifp;
+ unsigned int cSegs = 0;
+ bool fDropIt = false, fActive;
+ PINTNETSG pSG;
+
+ VBOXCURVNET_SET(ifp->if_vnet);
+ vboxNetFltRetain(pThis, true /* fBusy */);
+ for (;;)
+ {
+ mtx_lock_spin(&pThis->u.s.outq.ifq_mtx);
+ _IF_DEQUEUE(&pThis->u.s.outq, m);
+ mtx_unlock_spin(&pThis->u.s.outq.ifq_mtx);
+ if (m == NULL)
+ break;
+
+ for (m0 = m; m0 != NULL; m0 = m0->m_next)
+ if (m0->m_len > 0)
+ cSegs++;
+
+#ifdef PADD_RUNT_FRAMES_FROM_HOST
+ if (m_length(m, NULL) < 60)
+ cSegs++;
+#endif
+ /* Create a copy and deliver to the virtual switch */
+ pSG = RTMemTmpAlloc(RT_UOFFSETOF_DYN(INTNETSG, aSegs[cSegs]));
+ vboxNetFltFreeBSDMBufToSG(pThis, m, pSG, cSegs, 0);
+ fDropIt = pThis->pSwitchPort->pfnRecv(pThis->pSwitchPort, NULL /* pvIf */, pSG, INTNETTRUNKDIR_HOST);
+ RTMemTmpFree(pSG);
+
+ if (fDropIt)
+ m_freem(m);
+ else
+ ether_output_frame(ifp, m);
+ }
+ vboxNetFltRelease(pThis, true /* fBusy */);
+ VBOXCURVNET_RESTORE();
+}
+
+/**
+ * Called to deliver a frame to either the host, the wire or both.
+ */
+int vboxNetFltPortOsXmit(PVBOXNETFLTINS pThis, void *pvIfData, PINTNETSG pSG, uint32_t fDst)
+{
+ NOREF(pvIfData);
+
+ void (*input_f)(struct ifnet *, struct mbuf *);
+ struct ifnet *ifp;
+ struct mbuf *m;
+ struct m_tag *mtag;
+ bool fActive;
+ int error;
+
+ ifp = ASMAtomicUoReadPtrT(&pThis->u.s.ifp, struct ifnet *);
+ VBOXCURVNET_SET(ifp->if_vnet);
+
+ if (fDst & INTNETTRUNKDIR_WIRE)
+ {
+ m = vboxNetFltFreeBSDSGMBufFromSG(pThis, pSG);
+ if (m == NULL)
+ return VERR_NO_MEMORY;
+ m = m_pullup(m, ETHER_HDR_LEN);
+ if (m == NULL)
+ return VERR_NO_MEMORY;
+
+ m->m_flags |= M_PKTHDR;
+ ether_output_frame(ifp, m);
+ }
+
+ if (fDst & INTNETTRUNKDIR_HOST)
+ {
+ m = vboxNetFltFreeBSDSGMBufFromSG(pThis, pSG);
+ if (m == NULL)
+ return VERR_NO_MEMORY;
+ m = m_pullup(m, ETHER_HDR_LEN);
+ if (m == NULL)
+ return VERR_NO_MEMORY;
+ /*
+ * Delivering packets to the host will be captured by the
+ * input hook. Tag the packet with a mbuf tag so that we
+ * can skip re-delivery of the packet to the guest during
+ * input hook processing.
+ */
+ mtag = m_tag_alloc(MTAG_VBOX, PACKET_TAG_VBOX, 0, M_NOWAIT);
+ if (mtag == NULL)
+ {
+ m_freem(m);
+ return VERR_NO_MEMORY;
+ }
+
+ m_tag_init(m);
+ m_tag_prepend(m, mtag);
+ m->m_flags |= M_PKTHDR;
+ m->m_pkthdr.rcvif = ifp;
+ ifp->if_input(ifp, m);
+ }
+ VBOXCURVNET_RESTORE();
+ return VINF_SUCCESS;
+}
+
+static bool vboxNetFltFreeBsdIsPromiscuous(PVBOXNETFLTINS pThis)
+{
+ /** @todo This isn't taking into account that we put the interface in
+ * promiscuous mode. */
+ return (pThis->u.s.flags & IFF_PROMISC) ? true : false;
+}
+
+int vboxNetFltOsInitInstance(PVBOXNETFLTINS pThis, void *pvContext)
+{
+ char nam[NG_NODESIZ];
+ struct ifnet *ifp;
+ node_p node;
+
+ VBOXCURVNET_SET_FROM_UCRED();
+ NOREF(pvContext);
+ ifp = ifunit(pThis->szName);
+ if (ifp == NULL)
+ return VERR_INTNET_FLT_IF_NOT_FOUND;
+
+ /* Create a new netgraph node for this instance */
+ if (ng_make_node_common(&ng_vboxnetflt_typestruct, &node) != 0)
+ return VERR_INTERNAL_ERROR;
+
+ RTSpinlockAcquire(pThis->hSpinlock);
+
+ ASMAtomicUoWritePtr(&pThis->u.s.ifp, ifp);
+ pThis->u.s.node = node;
+ bcopy(IF_LLADDR(ifp), &pThis->u.s.MacAddr, ETHER_ADDR_LEN);
+ ASMAtomicUoWriteBool(&pThis->fDisconnectedFromHost, false);
+
+ /* Initialize deferred input queue */
+ bzero(&pThis->u.s.inq, sizeof(struct ifqueue));
+ mtx_init(&pThis->u.s.inq.ifq_mtx, "vboxnetflt inq", NULL, MTX_SPIN);
+ TASK_INIT(&pThis->u.s.tskin, 0, vboxNetFltFreeBSDinput, pThis);
+
+ /* Initialize deferred output queue */
+ bzero(&pThis->u.s.outq, sizeof(struct ifqueue));
+ mtx_init(&pThis->u.s.outq.ifq_mtx, "vboxnetflt outq", NULL, MTX_SPIN);
+ TASK_INIT(&pThis->u.s.tskout, 0, vboxNetFltFreeBSDoutput, pThis);
+
+ RTSpinlockRelease(pThis->hSpinlock);
+
+ NG_NODE_SET_PRIVATE(node, pThis);
+
+ /* Attempt to name it vboxnetflt_<ifname> */
+ snprintf(nam, NG_NODESIZ, "vboxnetflt_%s", pThis->szName);
+ ng_name_node(node, nam);
+
+ /* Report MAC address, promiscuous mode and GSO capabilities. */
+ /** @todo keep these reports up to date, either by polling for changes or
+ * intercept some control flow if possible. */
+ if (vboxNetFltTryRetainBusyNotDisconnected(pThis))
+ {
+ Assert(pThis->pSwitchPort);
+ pThis->pSwitchPort->pfnReportMacAddress(pThis->pSwitchPort, &pThis->u.s.MacAddr);
+ pThis->pSwitchPort->pfnReportPromiscuousMode(pThis->pSwitchPort, vboxNetFltFreeBsdIsPromiscuous(pThis));
+ pThis->pSwitchPort->pfnReportGsoCapabilities(pThis->pSwitchPort, 0, INTNETTRUNKDIR_WIRE | INTNETTRUNKDIR_HOST);
+ pThis->pSwitchPort->pfnReportNoPreemptDsts(pThis->pSwitchPort, 0 /* none */);
+ vboxNetFltRelease(pThis, true /*fBusy*/);
+ }
+ VBOXCURVNET_RESTORE();
+
+ return VINF_SUCCESS;
+}
+
+bool vboxNetFltOsMaybeRediscovered(PVBOXNETFLTINS pThis)
+{
+ struct ifnet *ifp, *ifp0;
+
+ ifp = ASMAtomicUoReadPtrT(&pThis->u.s.ifp, struct ifnet *);
+ VBOXCURVNET_SET(ifp->if_vnet);
+ /*
+ * Attempt to check if the interface is still there and re-initialize if
+ * something has changed.
+ */
+ ifp0 = ifunit(pThis->szName);
+ if (ifp != ifp0)
+ {
+ ASMAtomicUoWriteBool(&pThis->fDisconnectedFromHost, true);
+ ng_rmnode_self(pThis->u.s.node);
+ pThis->u.s.node = NULL;
+ }
+ VBOXCURVNET_RESTORE();
+
+ if (ifp0 != NULL)
+ {
+ vboxNetFltOsDeleteInstance(pThis);
+ vboxNetFltOsInitInstance(pThis, NULL);
+ }
+
+ return !ASMAtomicUoReadBool(&pThis->fDisconnectedFromHost);
+}
+
+void vboxNetFltOsDeleteInstance(PVBOXNETFLTINS pThis)
+{
+
+ taskqueue_drain(taskqueue_fast, &pThis->u.s.tskin);
+ taskqueue_drain(taskqueue_fast, &pThis->u.s.tskout);
+
+ mtx_destroy(&pThis->u.s.inq.ifq_mtx);
+ mtx_destroy(&pThis->u.s.outq.ifq_mtx);
+
+ VBOXCURVNET_SET_FROM_UCRED();
+ if (pThis->u.s.node != NULL)
+ ng_rmnode_self(pThis->u.s.node);
+ VBOXCURVNET_RESTORE();
+ pThis->u.s.node = NULL;
+}
+
+int vboxNetFltOsPreInitInstance(PVBOXNETFLTINS pThis)
+{
+
+ pThis->u.s.ifp = NULL;
+ pThis->u.s.flags = 0;
+ pThis->u.s.node = NULL;
+ return VINF_SUCCESS;
+}
+
+void vboxNetFltPortOsSetActive(PVBOXNETFLTINS pThis, bool fActive)
+{
+ struct ifnet *ifp;
+ struct ifreq ifreq;
+ int error;
+ node_p node;
+ struct ng_mesg *msg;
+ struct ngm_connect *con;
+ struct ngm_rmhook *rm;
+ char path[NG_PATHSIZ];
+
+ Log(("%s: fActive:%d\n", __func__, fActive));
+
+ ifp = ASMAtomicUoReadPtrT(&pThis->u.s.ifp, struct ifnet *);
+ VBOXCURVNET_SET(ifp->if_vnet);
+ node = ASMAtomicUoReadPtrT(&pThis->u.s.node, node_p);
+
+ memset(&ifreq, 0, sizeof(struct ifreq));
+ /* Activate interface */
+ if (fActive)
+ {
+ pThis->u.s.flags = ifp->if_flags;
+ ifpromisc(ifp, 1);
+
+ /* ng_ether nodes are named after the interface name */
+ snprintf(path, sizeof(path), "%s:", ifp->if_xname);
+
+ /*
+ * Send a netgraph connect message to the ng_ether node
+ * assigned to the bridged interface. Connecting
+ * the hooks 'lower' (ng_ether) to out 'input'.
+ */
+ NG_MKMESSAGE(msg, NGM_GENERIC_COOKIE, NGM_CONNECT,
+ sizeof(struct ngm_connect), M_NOWAIT);
+ if (msg == NULL)
+ return;
+ con = (struct ngm_connect *)msg->data;
+ snprintf(con->path, NG_PATHSIZ, "vboxnetflt_%s:", ifp->if_xname);
+ strlcpy(con->ourhook, "lower", NG_HOOKSIZ);
+ strlcpy(con->peerhook, "input", NG_HOOKSIZ);
+ NG_SEND_MSG_PATH(error, node, msg, path, 0);
+
+ /*
+ * Do the same for the hooks 'upper' (ng_ether) and our
+ * 'output' hook.
+ */
+ NG_MKMESSAGE(msg, NGM_GENERIC_COOKIE, NGM_CONNECT,
+ sizeof(struct ngm_connect), M_NOWAIT);
+ if (msg == NULL)
+ return;
+ con = (struct ngm_connect *)msg->data;
+ snprintf(con->path, NG_PATHSIZ, "vboxnetflt_%s:",
+ ifp->if_xname);
+ strlcpy(con->ourhook, "upper", sizeof(con->ourhook));
+ strlcpy(con->peerhook, "output", sizeof(con->peerhook));
+ NG_SEND_MSG_PATH(error, node, msg, path, 0);
+ }
+ else
+ {
+ /* De-activate interface */
+ pThis->u.s.flags = 0;
+ ifpromisc(ifp, 0);
+
+ /* Disconnect msgs are addressed to ourself */
+ snprintf(path, sizeof(path), "vboxnetflt_%s:", ifp->if_xname);
+
+ /*
+ * Send a netgraph message to disconnect our 'input' hook
+ */
+ NG_MKMESSAGE(msg, NGM_GENERIC_COOKIE, NGM_RMHOOK,
+ sizeof(struct ngm_rmhook), M_NOWAIT);
+ if (msg == NULL)
+ return;
+ rm = (struct ngm_rmhook *)msg->data;
+ strlcpy(rm->ourhook, "input", NG_HOOKSIZ);
+ NG_SEND_MSG_PATH(error, node, msg, path, 0);
+
+ /*
+ * Send a netgraph message to disconnect our 'output' hook
+ */
+ NG_MKMESSAGE(msg, NGM_GENERIC_COOKIE, NGM_RMHOOK,
+ sizeof(struct ngm_rmhook), M_NOWAIT);
+ if (msg == NULL)
+ return;
+ rm = (struct ngm_rmhook *)msg->data;
+ strlcpy(rm->ourhook, "output", NG_HOOKSIZ);
+ NG_SEND_MSG_PATH(error, node, msg, path, 0);
+ }
+ VBOXCURVNET_RESTORE();
+}
+
+int vboxNetFltOsDisconnectIt(PVBOXNETFLTINS pThis)
+{
+ return VINF_SUCCESS;
+}
+
+int vboxNetFltOsConnectIt(PVBOXNETFLTINS pThis)
+{
+ return VINF_SUCCESS;
+}
+
+void vboxNetFltPortOsNotifyMacAddress(PVBOXNETFLTINS pThis, void *pvIfData, PCRTMAC pMac)
+{
+ NOREF(pThis); NOREF(pvIfData); NOREF(pMac);
+}
+
+int vboxNetFltPortOsConnectInterface(PVBOXNETFLTINS pThis, void *pvIf, void **ppvIfData)
+{
+ /* Nothing to do */
+ NOREF(pThis); NOREF(pvIf); NOREF(ppvIfData);
+ return VINF_SUCCESS;
+}
+
+int vboxNetFltPortOsDisconnectInterface(PVBOXNETFLTINS pThis, void *pvIfData)
+{
+ /* Nothing to do */
+ NOREF(pThis); NOREF(pvIfData);
+ return VINF_SUCCESS;
+}
+
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/freebsd/files_vboxnetflt b/src/VBox/HostDrivers/VBoxNetFlt/freebsd/files_vboxnetflt
new file mode 100755
index 00000000..602b72e4
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/freebsd/files_vboxnetflt
@@ -0,0 +1,99 @@
+#!/bin/sh
+# $Id: files_vboxnetflt $
+## @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 <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+VBOX_VBOXNETFLT_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/intnetinline.h=>include/VBox/intnetinline.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/VBoxNetFlt/freebsd/VBoxNetFlt-freebsd.c=>VBoxNetFlt-freebsd.c \
+ ${PATH_ROOT}/src/VBox/HostDrivers/VBoxNetFlt/VBoxNetFlt.c=>VBoxNetFlt.c \
+ ${PATH_ROOT}/src/VBox/HostDrivers/VBoxNetFlt/VBoxNetFltInternal.h=>VBoxNetFltInternal.h \
+ ${PATH_ROOT}/src/VBox/HostDrivers/Support/SUPDrvIDC.h=>SUPDrvIDC.h \
+ ${PATH_ROOT}/src/VBox/HostDrivers/Support/SUPR0IdcClient.c=>SUPR0IdcClient.c \
+ ${PATH_ROOT}/src/VBox/HostDrivers/Support/SUPR0IdcClientComponent.c=>SUPR0IdcClientComponent.c \
+ ${PATH_ROOT}/src/VBox/HostDrivers/Support/SUPR0IdcClientInternal.h=>SUPR0IdcClientInternal.h \
+ ${PATH_ROOT}/src/VBox/HostDrivers/Support/freebsd/SUPR0IdcClient-freebsd.c=>SUPR0IdcClient-freebsd.c \
+ ${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/VBoxNetFlt/linux/Makefile b/src/VBox/HostDrivers/VBoxNetFlt/linux/Makefile
new file mode 100644
index 00000000..9c5d5b80
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/linux/Makefile
@@ -0,0 +1,81 @@
+# $Id: Makefile $
+## @file
+# Makefile for the VirtualBox Linux Host Network Filter 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 <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+# Linux kbuild sets this to our source directory if we are called from there
+obj ?= $(CURDIR)
+include $(obj)/Makefile-header.gmk
+VBOXNETFLT_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 $(VBOXNETFLT_DIR)/../vboxdrv/Module.symvers)
+endif
+
+VBOXMOD_NAME = vboxnetflt
+VBOXMOD_OBJS = \
+ linux/VBoxNetFlt-linux.o \
+ VBoxNetFlt.o \
+ SUPR0IdcClient.o \
+ SUPR0IdcClientComponent.o \
+ linux/SUPR0IdcClient-linux.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 = \
+ $(VBOXNETFLT_DIR) \
+ $(VBOXNETFLT_DIR)include \
+ $(VBOXNETFLT_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 $(VBOXNETFLT_DIR)include/VBox/SUPDrvMangling.h -fno-pie -Wno-declaration-after-statement
+
+include $(obj)/Makefile-footer.gmk
+
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/linux/Makefile.kup b/src/VBox/HostDrivers/VBoxNetFlt/linux/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/linux/Makefile.kup
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/linux/VBoxNetFlt-linux.c b/src/VBox/HostDrivers/VBoxNetFlt/linux/VBoxNetFlt-linux.c
new file mode 100644
index 00000000..eaf07e8c
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/linux/VBoxNetFlt-linux.c
@@ -0,0 +1,2609 @@
+/* $Id: VBoxNetFlt-linux.c $ */
+/** @file
+ * VBoxNetFlt - Network Filter Driver (Host), Linux Specific Code.
+ */
+
+/*
+ * 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 <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_NET_FLT_DRV
+#define VBOXNETFLT_LINUX_NO_XMIT_QUEUE
+#include "the-linux-kernel.h"
+#include "version-generated.h"
+#include "revision-generated.h"
+#include "product-generated.h"
+#if RTLNX_VER_MIN(2,6,24)
+# include <linux/nsproxy.h>
+#endif
+#if RTLNX_VER_MIN(6,4,10) || RTLNX_RHEL_RANGE(9,4, 9,99)
+# include <net/gso.h>
+#endif
+#include <linux/netdevice.h>
+#if RTLNX_VER_MAX(2,6,29) || RTLNX_VER_MIN(5,11,0)
+# include <linux/ethtool.h>
+#endif
+#include <linux/etherdevice.h>
+#include <linux/rtnetlink.h>
+#include <linux/miscdevice.h>
+#include <linux/inetdevice.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/if_vlan.h>
+#if RTLNX_VER_MIN(4,5,0)
+# include <uapi/linux/pkt_cls.h>
+#endif
+#include <net/ipv6.h>
+#include <net/if_inet6.h>
+#include <net/addrconf.h>
+
+#include <VBox/log.h>
+#include <VBox/err.h>
+#include <VBox/intnetinline.h>
+#include <VBox/vmm/pdmnetinline.h>
+#include <VBox/param.h>
+#include <iprt/alloca.h>
+#include <iprt/assert.h>
+#include <iprt/spinlock.h>
+#include <iprt/semaphore.h>
+#include <iprt/initterm.h>
+#include <iprt/process.h>
+#include <iprt/mem.h>
+#include <iprt/net.h>
+#include <iprt/log.h>
+#include <iprt/mp.h>
+#include <iprt/mem.h>
+#include <iprt/time.h>
+
+#define VBOXNETFLT_OS_SPECFIC 1
+#include "../VBoxNetFltInternal.h"
+
+typedef struct VBOXNETFLTNOTIFIER {
+ struct notifier_block Notifier;
+ PVBOXNETFLTINS pThis;
+} VBOXNETFLTNOTIFIER;
+typedef struct VBOXNETFLTNOTIFIER *PVBOXNETFLTNOTIFIER;
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#define VBOX_FLT_NB_TO_INST(pNB) RT_FROM_MEMBER(pNB, VBOXNETFLTINS, u.s.Notifier)
+#define VBOX_FLT_PT_TO_INST(pPT) RT_FROM_MEMBER(pPT, VBOXNETFLTINS, u.s.PacketType)
+#ifndef VBOXNETFLT_LINUX_NO_XMIT_QUEUE
+# define VBOX_FLT_XT_TO_INST(pXT) RT_FROM_MEMBER(pXT, VBOXNETFLTINS, u.s.XmitTask)
+#endif
+
+#if RTLNX_VER_MIN(3,11,0)
+# define VBOX_NETDEV_NOTIFIER_INFO_TO_DEV(ptr) netdev_notifier_info_to_dev(ptr)
+#else
+# define VBOX_NETDEV_NOTIFIER_INFO_TO_DEV(ptr) ((struct net_device *)ptr)
+#endif
+
+#if RTLNX_VER_MIN(3,5,0)
+# define VBOX_SKB_KMAP_FRAG(frag) kmap_atomic(skb_frag_page(frag))
+# define VBOX_SKB_KUNMAP_FRAG(vaddr) kunmap_atomic(vaddr)
+#else
+# if RTLNX_VER_MIN(3,2,0)
+# define VBOX_SKB_KMAP_FRAG(frag) kmap_atomic(skb_frag_page(frag), KM_SKB_DATA_SOFTIRQ)
+# define VBOX_SKB_KUNMAP_FRAG(vaddr) kunmap_atomic(vaddr, KM_SKB_DATA_SOFTIRQ)
+# else
+# define VBOX_SKB_KMAP_FRAG(frag) kmap_atomic(frag->page, KM_SKB_DATA_SOFTIRQ)
+# define VBOX_SKB_KUNMAP_FRAG(vaddr) kunmap_atomic(vaddr, KM_SKB_DATA_SOFTIRQ)
+# endif
+#endif
+
+#if RTLNX_VER_MIN(2,6,34)
+# define VBOX_NETDEV_NAME(dev) netdev_name(dev)
+#else
+# define VBOX_NETDEV_NAME(dev) ((dev)->reg_state != NETREG_REGISTERED ? "(unregistered net_device)" : (dev)->name)
+#endif
+
+#if RTLNX_VER_MIN(2,6,25)
+# define VBOX_IPV4_IS_LOOPBACK(addr) ipv4_is_loopback(addr)
+# define VBOX_IPV4_IS_LINKLOCAL_169(addr) ipv4_is_linklocal_169(addr)
+#else
+# define VBOX_IPV4_IS_LOOPBACK(addr) ((addr & htonl(IN_CLASSA_NET)) == htonl(0x7f000000))
+# define VBOX_IPV4_IS_LINKLOCAL_169(addr) ((addr & htonl(IN_CLASSB_NET)) == htonl(0xa9fe0000))
+#endif
+
+#if RTLNX_VER_MIN(2,6,22)
+# define VBOX_SKB_RESET_NETWORK_HDR(skb) skb_reset_network_header(skb)
+# define VBOX_SKB_RESET_MAC_HDR(skb) skb_reset_mac_header(skb)
+# define VBOX_SKB_CSUM_OFFSET(skb) skb->csum_offset
+#else
+# define VBOX_SKB_RESET_NETWORK_HDR(skb) skb->nh.raw = skb->data
+# define VBOX_SKB_RESET_MAC_HDR(skb) skb->mac.raw = skb->data
+# define VBOX_SKB_CSUM_OFFSET(skb) skb->csum
+#endif
+
+#if RTLNX_VER_MIN(2,6,19)
+# define VBOX_SKB_CHECKSUM_HELP(skb) skb_checksum_help(skb)
+#else
+# define CHECKSUM_PARTIAL CHECKSUM_HW
+# if RTLNX_VER_MIN(2,6,10)
+# define VBOX_SKB_CHECKSUM_HELP(skb) skb_checksum_help(skb, 0)
+# else
+# if RTLNX_VER_MIN(2,6,7)
+# define VBOX_SKB_CHECKSUM_HELP(skb) skb_checksum_help(&skb, 0)
+# else
+# define VBOX_SKB_CHECKSUM_HELP(skb) (!skb_checksum_help(skb))
+# endif
+/* Versions prior 2.6.10 use stats for both bstats and qstats */
+# define bstats stats
+# define qstats stats
+# endif
+#endif
+
+#if RTLNX_VER_MIN(3,20,0) || RTLNX_RHEL_RANGE(7,2, 8,0) || RTLNX_RHEL_RANGE(6,8, 7,0)
+# define VBOX_HAVE_SKB_VLAN
+#endif
+
+#ifdef VBOX_HAVE_SKB_VLAN
+# define vlan_tx_tag_get(skb) skb_vlan_tag_get(skb)
+# define vlan_tx_tag_present(skb) skb_vlan_tag_present(skb)
+#endif
+
+#ifndef NET_IP_ALIGN
+# define NET_IP_ALIGN 2
+#endif
+
+#if 1
+/** Create scatter / gather segments for fragments. When not used, we will
+ * linearize the socket buffer before creating the internal networking SG. */
+# define VBOXNETFLT_SG_SUPPORT 1
+#endif
+
+#if RTLNX_VER_MIN(2,6,18)
+
+/** Indicates that the linux kernel may send us GSO frames. */
+# define VBOXNETFLT_WITH_GSO 1
+
+/** This enables or disables the transmitting of GSO frame from the internal
+ * network and to the host. */
+# define VBOXNETFLT_WITH_GSO_XMIT_HOST 1
+
+# if 0 /** @todo This is currently disable because it causes performance loss of 5-10%. */
+/** This enables or disables the transmitting of GSO frame from the internal
+ * network and to the wire. */
+# define VBOXNETFLT_WITH_GSO_XMIT_WIRE 1
+# endif
+
+/** This enables or disables the forwarding/flooding of GSO frame from the host
+ * to the internal network. */
+# define VBOXNETFLT_WITH_GSO_RECV 1
+
+#endif /* RTLNX_VER_MIN(2,6,18) */
+
+#if RTLNX_VER_MIN(2,6,29)
+/** This enables or disables handling of GSO frames coming from the wire (GRO). */
+# define VBOXNETFLT_WITH_GRO 1
+#endif
+
+/*
+ * GRO support was backported to RHEL 5.4
+ */
+#if RTLNX_RHEL_MAJ_PREREQ(5, 4)
+# define VBOXNETFLT_WITH_GRO 1
+#endif
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static int __init VBoxNetFltLinuxInit(void);
+static void __exit VBoxNetFltLinuxUnload(void);
+static void vboxNetFltLinuxForwardToIntNet(PVBOXNETFLTINS pThis, struct sk_buff *pBuf);
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/**
+ * The (common) global data.
+ */
+static VBOXNETFLTGLOBALS g_VBoxNetFltGlobals;
+
+module_init(VBoxNetFltLinuxInit);
+module_exit(VBoxNetFltLinuxUnload);
+
+MODULE_AUTHOR(VBOX_VENDOR);
+MODULE_DESCRIPTION(VBOX_PRODUCT " Network Filter Driver");
+MODULE_LICENSE("GPL");
+#ifdef MODULE_VERSION
+MODULE_VERSION(VBOX_VERSION_STRING " r" RT_XSTR(VBOX_SVN_REV) " (" RT_XSTR(INTNETTRUNKIFPORT_VERSION) ")");
+#endif
+
+
+#if RTLNX_VER_MAX(2,6,12) && defined(LOG_ENABLED)
+unsigned dev_get_flags(const struct net_device *dev)
+{
+ unsigned flags;
+
+ flags = (dev->flags & ~(IFF_PROMISC |
+ IFF_ALLMULTI |
+ IFF_RUNNING)) |
+ (dev->gflags & (IFF_PROMISC |
+ IFF_ALLMULTI));
+
+ if (netif_running(dev) && netif_carrier_ok(dev))
+ flags |= IFF_RUNNING;
+
+ return flags;
+}
+#endif /* RTLNX_VER_MAX(2,6,12) */
+
+
+/**
+ * Initialize module.
+ *
+ * @returns appropriate status code.
+ */
+static int __init VBoxNetFltLinuxInit(void)
+{
+ int rc;
+ /*
+ * Initialize IPRT.
+ */
+ rc = RTR0Init(0);
+ if (RT_SUCCESS(rc))
+ {
+ Log(("VBoxNetFltLinuxInit\n"));
+
+ /*
+ * Initialize the globals and connect to the support driver.
+ *
+ * This will call back vboxNetFltOsOpenSupDrv (and maybe vboxNetFltOsCloseSupDrv)
+ * for establishing the connect to the support driver.
+ */
+ memset(&g_VBoxNetFltGlobals, 0, sizeof(g_VBoxNetFltGlobals));
+ rc = vboxNetFltInitGlobalsAndIdc(&g_VBoxNetFltGlobals);
+ if (RT_SUCCESS(rc))
+ {
+ LogRel(("VBoxNetFlt: Successfully started.\n"));
+ return 0;
+ }
+
+ LogRel(("VBoxNetFlt: failed to initialize device extension (rc=%d)\n", rc));
+ RTR0Term();
+ }
+ else
+ LogRel(("VBoxNetFlt: failed to initialize IPRT (rc=%d)\n", rc));
+
+ memset(&g_VBoxNetFltGlobals, 0, sizeof(g_VBoxNetFltGlobals));
+ return -RTErrConvertToErrno(rc);
+}
+
+
+/**
+ * Unload the module.
+ *
+ * @todo We have to prevent this if we're busy!
+ */
+static void __exit VBoxNetFltLinuxUnload(void)
+{
+ int rc;
+ Log(("VBoxNetFltLinuxUnload\n"));
+ Assert(vboxNetFltCanUnload(&g_VBoxNetFltGlobals));
+
+ /*
+ * Undo the work done during start (in reverse order).
+ */
+ rc = vboxNetFltTryDeleteIdcAndGlobals(&g_VBoxNetFltGlobals);
+ AssertRC(rc); NOREF(rc);
+
+ RTR0Term();
+
+ memset(&g_VBoxNetFltGlobals, 0, sizeof(g_VBoxNetFltGlobals));
+
+ Log(("VBoxNetFltLinuxUnload - done\n"));
+}
+
+
+/**
+ * We filter traffic from the host to the internal network
+ * before it reaches the NIC driver.
+ *
+ * The current code uses a very ugly hack overriding hard_start_xmit
+ * callback in the device structure, but it has been shown to give us a
+ * performance boost of 60-100% though. Eventually we have to find some
+ * less hacky way of getting this job done.
+ */
+#define VBOXNETFLT_WITH_HOST2WIRE_FILTER
+
+#ifdef VBOXNETFLT_WITH_HOST2WIRE_FILTER
+
+# if RTLNX_VER_MAX(2,6,29)
+
+typedef struct ethtool_ops OVR_OPSTYPE;
+# define OVR_OPS ethtool_ops
+# define OVR_XMIT pfnStartXmit
+
+# else /* RTLNX_VER_MIN(2,6,29) */
+
+typedef struct net_device_ops OVR_OPSTYPE;
+# define OVR_OPS netdev_ops
+# define OVR_XMIT pOrgOps->ndo_start_xmit
+
+# endif /* RTLNX_VER_MIN(2,6,29) */
+
+/**
+ * The overridden net_device_ops of the device we're attached to.
+ *
+ * As there is no net_device_ops structure in pre-2.6.29 kernels we override
+ * ethtool_ops instead along with hard_start_xmit callback in net_device
+ * structure.
+ *
+ * This is a very dirty hack that was created to explore how much we can improve
+ * the host to guest transfers by not CC'ing the NIC. It turns out to be
+ * the only way to filter outgoing packets for devices without TX queue.
+ */
+typedef struct VBoxNetDeviceOpsOverride
+{
+ /** Our overridden ops. */
+ OVR_OPSTYPE Ops;
+ /** Magic word. */
+ uint32_t u32Magic;
+ /** Pointer to the original ops. */
+ OVR_OPSTYPE const *pOrgOps;
+# if RTLNX_VER_MAX(2,6,29)
+ /** Pointer to the original hard_start_xmit function. */
+ int (*pfnStartXmit)(struct sk_buff *pSkb, struct net_device *pDev);
+# endif /* RTLNX_VER_MAX(2,6,29) */
+ /** Pointer to the net filter instance. */
+ PVBOXNETFLTINS pVBoxNetFlt;
+ /** The number of filtered packages. */
+ uint64_t cFiltered;
+ /** The total number of packets */
+ uint64_t cTotal;
+} VBOXNETDEVICEOPSOVERRIDE, *PVBOXNETDEVICEOPSOVERRIDE;
+/** VBOXNETDEVICEOPSOVERRIDE::u32Magic value. */
+#define VBOXNETDEVICEOPSOVERRIDE_MAGIC UINT32_C(0x00c0ffee)
+
+/**
+ * ndo_start_xmit wrapper that drops packets that shouldn't go to the wire
+ * because they belong on the internal network.
+ *
+ * @returns NETDEV_TX_XXX.
+ * @param pSkb The socket buffer to transmit.
+ * @param pDev The net device.
+ */
+static int vboxNetFltLinuxStartXmitFilter(struct sk_buff *pSkb, struct net_device *pDev)
+{
+ PVBOXNETDEVICEOPSOVERRIDE pOverride = (PVBOXNETDEVICEOPSOVERRIDE)pDev->OVR_OPS;
+ uint8_t abHdrBuf[sizeof(RTNETETHERHDR) + sizeof(uint32_t) + RTNETIPV4_MIN_LEN];
+ PCRTNETETHERHDR pEtherHdr;
+ PINTNETTRUNKSWPORT pSwitchPort;
+ uint32_t cbHdrs;
+
+
+ /*
+ * Validate the override structure.
+ *
+ * Note! We're racing vboxNetFltLinuxUnhookDev here. If this was supposed
+ * to be production quality code, we would have to be much more
+ * careful here and avoid the race.
+ */
+ if ( !RT_VALID_PTR(pOverride)
+ || pOverride->u32Magic != VBOXNETDEVICEOPSOVERRIDE_MAGIC
+# if RTLNX_VER_MIN(2,6,29)
+ || !RT_VALID_PTR(pOverride->pOrgOps)
+# endif
+ )
+ {
+ printk("vboxNetFltLinuxStartXmitFilter: bad override %p\n", pOverride);
+ dev_kfree_skb(pSkb);
+ return NETDEV_TX_OK;
+ }
+ pOverride->cTotal++;
+
+ /*
+ * Do the filtering base on the default OUI of our virtual NICs
+ *
+ * Note! In a real solution, we would ask the switch whether the
+ * destination MAC is 100% to be on the internal network and then
+ * drop it.
+ */
+ cbHdrs = skb_headlen(pSkb);
+ cbHdrs = RT_MIN(cbHdrs, sizeof(abHdrBuf));
+ pEtherHdr = (PCRTNETETHERHDR)skb_header_pointer(pSkb, 0, cbHdrs, &abHdrBuf[0]);
+ if ( pEtherHdr
+ && RT_VALID_PTR(pOverride->pVBoxNetFlt)
+ && (pSwitchPort = pOverride->pVBoxNetFlt->pSwitchPort) != NULL
+ && RT_VALID_PTR(pSwitchPort)
+ && cbHdrs >= 6)
+ {
+ INTNETSWDECISION enmDecision;
+
+ /** @todo consider reference counting, etc. */
+ enmDecision = pSwitchPort->pfnPreRecv(pSwitchPort, pEtherHdr, cbHdrs, INTNETTRUNKDIR_HOST);
+ if (enmDecision == INTNETSWDECISION_INTNET)
+ {
+ dev_kfree_skb(pSkb);
+ pOverride->cFiltered++;
+ return NETDEV_TX_OK;
+ }
+ }
+
+ return pOverride->OVR_XMIT(pSkb, pDev);
+}
+
+/**
+ * Hooks the device ndo_start_xmit operation of the device.
+ *
+ * @param pThis The net filter instance.
+ * @param pDev The net device.
+ */
+static void vboxNetFltLinuxHookDev(PVBOXNETFLTINS pThis, struct net_device *pDev)
+{
+ PVBOXNETDEVICEOPSOVERRIDE pOverride;
+
+ /* Cancel override if ethtool_ops is missing (host-only case, @bugref{5712}) */
+ if (!RT_VALID_PTR(pDev->OVR_OPS))
+ return;
+ pOverride = RTMemAlloc(sizeof(*pOverride));
+ if (!pOverride)
+ return;
+ pOverride->pOrgOps = pDev->OVR_OPS;
+ pOverride->Ops = *pDev->OVR_OPS;
+# if RTLNX_VER_MAX(2,6,29)
+ pOverride->pfnStartXmit = pDev->hard_start_xmit;
+# else /* RTLNX_VER_MIN(2,6,29) */
+ pOverride->Ops.ndo_start_xmit = vboxNetFltLinuxStartXmitFilter;
+# endif /* RTLNX_VER_MIN(2,6,29) */
+ pOverride->u32Magic = VBOXNETDEVICEOPSOVERRIDE_MAGIC;
+ pOverride->cTotal = 0;
+ pOverride->cFiltered = 0;
+ pOverride->pVBoxNetFlt = pThis;
+
+ RTSpinlockAcquire(pThis->hSpinlock); /* (this isn't necessary, but so what) */
+ ASMAtomicWritePtr((void * volatile *)&pDev->OVR_OPS, pOverride);
+# if RTLNX_VER_MAX(2,6,29)
+ ASMAtomicXchgPtr((void * volatile *)&pDev->hard_start_xmit, vboxNetFltLinuxStartXmitFilter);
+# endif /* RTLNX_VER_MAX(2,6,29) */
+ RTSpinlockRelease(pThis->hSpinlock);
+}
+
+/**
+ * Undos what vboxNetFltLinuxHookDev did.
+ *
+ * @param pThis The net filter instance.
+ * @param pDev The net device. Can be NULL, in which case
+ * we'll try retrieve it from @a pThis.
+ */
+static void vboxNetFltLinuxUnhookDev(PVBOXNETFLTINS pThis, struct net_device *pDev)
+{
+ PVBOXNETDEVICEOPSOVERRIDE pOverride;
+
+ RTSpinlockAcquire(pThis->hSpinlock);
+ if (!pDev)
+ pDev = ASMAtomicUoReadPtrT(&pThis->u.s.pDev, struct net_device *);
+ if (RT_VALID_PTR(pDev))
+ {
+ pOverride = (PVBOXNETDEVICEOPSOVERRIDE)pDev->OVR_OPS;
+ if ( RT_VALID_PTR(pOverride)
+ && pOverride->u32Magic == VBOXNETDEVICEOPSOVERRIDE_MAGIC
+ && RT_VALID_PTR(pOverride->pOrgOps)
+ )
+ {
+# if RTLNX_VER_MAX(2,6,29)
+ ASMAtomicWritePtr((void * volatile *)&pDev->hard_start_xmit, pOverride->pfnStartXmit);
+# endif /* RTLNX_VER_MAX(2,6,29) */
+ ASMAtomicWritePtr((void const * volatile *)&pDev->OVR_OPS, pOverride->pOrgOps);
+ ASMAtomicWriteU32(&pOverride->u32Magic, 0);
+ }
+ else
+ pOverride = NULL;
+ }
+ else
+ pOverride = NULL;
+ RTSpinlockRelease(pThis->hSpinlock);
+
+ if (pOverride)
+ {
+ printk("vboxnetflt: %llu out of %llu packets were not sent (directed to host)\n", pOverride->cFiltered, pOverride->cTotal);
+ RTMemFree(pOverride);
+ }
+}
+
+#endif /* VBOXNETFLT_WITH_HOST2WIRE_FILTER */
+
+
+/**
+ * Reads and retains the host interface handle.
+ *
+ * @returns The handle, NULL if detached.
+ * @param pThis
+ */
+DECLINLINE(struct net_device *) vboxNetFltLinuxRetainNetDev(PVBOXNETFLTINS pThis)
+{
+#if 0
+ struct net_device *pDev = NULL;
+
+ Log(("vboxNetFltLinuxRetainNetDev\n"));
+ /*
+ * Be careful here to avoid problems racing the detached callback.
+ */
+ RTSpinlockAcquire(pThis->hSpinlock);
+ if (!ASMAtomicUoReadBool(&pThis->fDisconnectedFromHost))
+ {
+ pDev = (struct net_device *)ASMAtomicUoReadPtr((void * volatile *)&pThis->u.s.pDev);
+ if (pDev)
+ {
+ dev_hold(pDev);
+ Log(("vboxNetFltLinuxRetainNetDev: Device %p(%s) retained. ref=%d\n",
+ pDev, pDev->name,
+#if RTLNX_VER_MIN(2,6,37)
+ netdev_refcnt_read(pDev)
+#else
+ atomic_read(&pDev->refcnt)
+#endif
+ ));
+ }
+ }
+ RTSpinlockRelease(pThis->hSpinlock);
+
+ Log(("vboxNetFltLinuxRetainNetDev - done\n"));
+ return pDev;
+#else
+ return ASMAtomicUoReadPtrT(&pThis->u.s.pDev, struct net_device *);
+#endif
+}
+
+
+/**
+ * Release the host interface handle previously retained
+ * by vboxNetFltLinuxRetainNetDev.
+ *
+ * @param pThis The instance.
+ * @param pDev The vboxNetFltLinuxRetainNetDev
+ * return value, NULL is fine.
+ */
+DECLINLINE(void) vboxNetFltLinuxReleaseNetDev(PVBOXNETFLTINS pThis, struct net_device *pDev)
+{
+#if 0
+ Log(("vboxNetFltLinuxReleaseNetDev\n"));
+ NOREF(pThis);
+ if (pDev)
+ {
+ dev_put(pDev);
+ Log(("vboxNetFltLinuxReleaseNetDev: Device %p(%s) released. ref=%d\n",
+ pDev, pDev->name,
+#if RTLNX_VER_MIN(2,6,37)
+ netdev_refcnt_read(pDev)
+#else
+ atomic_read(&pDev->refcnt)
+#endif
+ ));
+ }
+ Log(("vboxNetFltLinuxReleaseNetDev - done\n"));
+#endif
+}
+
+#define VBOXNETFLT_CB_TAG(skb) (0xA1C90000 | (skb->dev->ifindex & 0xFFFF))
+#define VBOXNETFLT_SKB_TAG(skb) (*(uint32_t*)&((skb)->cb[sizeof((skb)->cb)-sizeof(uint32_t)]))
+
+/**
+ * Checks whether this is an mbuf created by vboxNetFltLinuxMBufFromSG,
+ * i.e. a buffer which we're pushing and should be ignored by the filter callbacks.
+ *
+ * @returns true / false accordingly.
+ * @param pBuf The sk_buff.
+ */
+DECLINLINE(bool) vboxNetFltLinuxSkBufIsOur(struct sk_buff *pBuf)
+{
+ return VBOXNETFLT_SKB_TAG(pBuf) == VBOXNETFLT_CB_TAG(pBuf);
+}
+
+
+/**
+ * Checks whether this SG list contains a GSO packet.
+ *
+ * @returns true / false accordingly.
+ * @param pSG The (scatter/)gather list.
+ */
+DECLINLINE(bool) vboxNetFltLinuxIsGso(PINTNETSG pSG)
+{
+#if defined(VBOXNETFLT_WITH_GSO_XMIT_WIRE) || defined(VBOXNETFLT_WITH_GSO_XMIT_HOST)
+ return !((PDMNETWORKGSOTYPE)pSG->GsoCtx.u8Type == PDMNETWORKGSOTYPE_INVALID);
+#else /* !VBOXNETFLT_WITH_GSO_XMIT_WIRE && !VBOXNETFLT_WITH_GSO_XMIT_HOST */
+ return false;
+#endif /* !VBOXNETFLT_WITH_GSO_XMIT_WIRE && !VBOXNETFLT_WITH_GSO_XMIT_HOST */
+}
+
+
+/**
+ * Find out the frame size (of a single segment in case of GSO frames).
+ *
+ * @returns the frame size.
+ * @param pSG The (scatter/)gather list.
+ */
+DECLINLINE(uint32_t) vboxNetFltLinuxFrameSize(PINTNETSG pSG)
+{
+ uint16_t u16Type = 0;
+ uint32_t cbVlanTag = 0;
+ if (pSG->aSegs[0].cb >= sizeof(RTNETETHERHDR))
+ u16Type = RT_BE2H_U16(((PCRTNETETHERHDR)pSG->aSegs[0].pv)->EtherType);
+ else if (pSG->cbTotal >= sizeof(RTNETETHERHDR))
+ {
+ uint32_t off = RT_UOFFSETOF(RTNETETHERHDR, EtherType);
+ uint32_t i;
+ for (i = 0; i < pSG->cSegsUsed; ++i)
+ {
+ if (off <= pSG->aSegs[i].cb)
+ {
+ if (off + sizeof(uint16_t) <= pSG->aSegs[i].cb)
+ u16Type = RT_BE2H_U16(*(uint16_t *)((uintptr_t)pSG->aSegs[i].pv + off));
+ else if (i + 1 < pSG->cSegsUsed)
+ u16Type = RT_BE2H_U16( ((uint16_t)( ((uint8_t *)pSG->aSegs[i].pv)[off] ) << 8)
+ + *(uint8_t *)pSG->aSegs[i + 1].pv); /* ASSUMES no empty segments! */
+ /* else: frame is too short. */
+ break;
+ }
+ off -= pSG->aSegs[i].cb;
+ }
+ }
+ if (u16Type == RTNET_ETHERTYPE_VLAN)
+ cbVlanTag = 4;
+ return (vboxNetFltLinuxIsGso(pSG) ? (uint32_t)pSG->GsoCtx.cbMaxSeg + pSG->GsoCtx.cbHdrsTotal : pSG->cbTotal) - cbVlanTag;
+}
+
+
+/**
+ * Internal worker that create a linux sk_buff for a
+ * (scatter/)gather list.
+ *
+ * @returns Pointer to the sk_buff.
+ * @param pThis The instance.
+ * @param pSG The (scatter/)gather list.
+ * @param fDstWire Set if the destination is the wire.
+ */
+static struct sk_buff *vboxNetFltLinuxSkBufFromSG(PVBOXNETFLTINS pThis, PINTNETSG pSG, bool fDstWire)
+{
+ struct sk_buff *pPkt;
+ struct net_device *pDev;
+#if defined(VBOXNETFLT_WITH_GSO_XMIT_WIRE) || defined(VBOXNETFLT_WITH_GSO_XMIT_HOST)
+ unsigned fGsoType = 0;
+#endif
+
+ if (pSG->cbTotal == 0)
+ {
+ LogRel(("VBoxNetFlt: Dropped empty packet coming from internal network.\n"));
+ return NULL;
+ }
+ Log5(("VBoxNetFlt: Packet to %s of %d bytes (frame=%d).\n", fDstWire?"wire":"host", pSG->cbTotal, vboxNetFltLinuxFrameSize(pSG)));
+ if (fDstWire && (vboxNetFltLinuxFrameSize(pSG) > ASMAtomicReadU32(&pThis->u.s.cbMtu) + 14))
+ {
+ static bool s_fOnce = true;
+ if (s_fOnce)
+ {
+ s_fOnce = false;
+ printk("VBoxNetFlt: Dropped over-sized packet (%d bytes) coming from internal network.\n", vboxNetFltLinuxFrameSize(pSG));
+ }
+ return NULL;
+ }
+
+ /** @todo We should use fragments mapping the SG buffers with large packets.
+ * 256 bytes seems to be the a threshold used a lot for this. It
+ * requires some nasty work on the intnet side though... */
+ /*
+ * Allocate a packet and copy over the data.
+ */
+ pDev = ASMAtomicUoReadPtrT(&pThis->u.s.pDev, struct net_device *);
+ pPkt = dev_alloc_skb(pSG->cbTotal + NET_IP_ALIGN);
+ if (RT_UNLIKELY(!pPkt))
+ {
+ Log(("vboxNetFltLinuxSkBufFromSG: Failed to allocate sk_buff(%u).\n", pSG->cbTotal));
+ pSG->pvUserData = NULL;
+ return NULL;
+ }
+ pPkt->dev = pDev;
+ pPkt->ip_summed = CHECKSUM_NONE;
+
+ /* Align IP header on 16-byte boundary: 2 + 14 (ethernet hdr size). */
+ skb_reserve(pPkt, NET_IP_ALIGN);
+
+ /* Copy the segments. */
+ skb_put(pPkt, pSG->cbTotal);
+ IntNetSgRead(pSG, pPkt->data);
+
+#if defined(VBOXNETFLT_WITH_GSO_XMIT_WIRE) || defined(VBOXNETFLT_WITH_GSO_XMIT_HOST)
+ /*
+ * Setup GSO if used by this packet.
+ */
+ switch ((PDMNETWORKGSOTYPE)pSG->GsoCtx.u8Type)
+ {
+ default:
+ AssertMsgFailed(("%u (%s)\n", pSG->GsoCtx.u8Type, PDMNetGsoTypeName((PDMNETWORKGSOTYPE)pSG->GsoCtx.u8Type) ));
+ RT_FALL_THRU();
+ case PDMNETWORKGSOTYPE_INVALID:
+ fGsoType = 0;
+ break;
+ case PDMNETWORKGSOTYPE_IPV4_TCP:
+ fGsoType = SKB_GSO_TCPV4;
+ break;
+ case PDMNETWORKGSOTYPE_IPV6_TCP:
+ fGsoType = SKB_GSO_TCPV6;
+ break;
+ }
+ if (fGsoType)
+ {
+ struct skb_shared_info *pShInfo = skb_shinfo(pPkt);
+
+ pShInfo->gso_type = fGsoType | SKB_GSO_DODGY;
+ pShInfo->gso_size = pSG->GsoCtx.cbMaxSeg;
+ pShInfo->gso_segs = PDMNetGsoCalcSegmentCount(&pSG->GsoCtx, pSG->cbTotal);
+
+ /*
+ * We need to set checksum fields even if the packet goes to the host
+ * directly as it may be immediately forwarded by IP layer @bugref{5020}.
+ */
+ Assert(skb_headlen(pPkt) >= pSG->GsoCtx.cbHdrsTotal);
+ pPkt->ip_summed = CHECKSUM_PARTIAL;
+# if RTLNX_VER_MIN(2,6,22)
+ pPkt->csum_start = skb_headroom(pPkt) + pSG->GsoCtx.offHdr2;
+ if (fGsoType & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6))
+ pPkt->csum_offset = RT_UOFFSETOF(RTNETTCP, th_sum);
+ else
+ pPkt->csum_offset = RT_UOFFSETOF(RTNETUDP, uh_sum);
+# else
+ pPkt->h.raw = pPkt->data + pSG->GsoCtx.offHdr2;
+ if (fGsoType & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6))
+ pPkt->csum = RT_UOFFSETOF(RTNETTCP, th_sum);
+ else
+ pPkt->csum = RT_UOFFSETOF(RTNETUDP, uh_sum);
+# endif
+ if (!fDstWire)
+ PDMNetGsoPrepForDirectUse(&pSG->GsoCtx, pPkt->data, pSG->cbTotal, PDMNETCSUMTYPE_PSEUDO);
+ }
+#endif /* VBOXNETFLT_WITH_GSO_XMIT_WIRE || VBOXNETFLT_WITH_GSO_XMIT_HOST */
+
+ /*
+ * Finish up the socket buffer.
+ */
+ pPkt->protocol = eth_type_trans(pPkt, pDev);
+ if (fDstWire)
+ {
+ VBOX_SKB_RESET_NETWORK_HDR(pPkt);
+
+ /* Restore ethernet header back. */
+ skb_push(pPkt, ETH_HLEN); /** @todo VLAN: +4 if VLAN? */
+ VBOX_SKB_RESET_MAC_HDR(pPkt);
+ }
+ VBOXNETFLT_SKB_TAG(pPkt) = VBOXNETFLT_CB_TAG(pPkt);
+
+ return pPkt;
+}
+
+
+/**
+ * Return the offset where to start checksum computation from.
+ *
+ * @returns the offset relative to pBuf->data.
+ * @param pBuf The socket buffer.
+ */
+DECLINLINE(unsigned) vboxNetFltLinuxGetChecksumStartOffset(struct sk_buff *pBuf)
+{
+#if RTLNX_VER_MIN(2,6,38)
+ return skb_checksum_start_offset(pBuf);
+#elif RTLNX_VER_MIN(2,6,22)
+ return pBuf->csum_start - skb_headroom(pBuf);
+#else
+ unsigned char *pTransportHdr = pBuf->h.raw;
+# if RTLNX_VER_MAX(2,6,19)
+ /*
+ * Try to work around the problem with CentOS 4.7 and 5.2 (2.6.9
+ * and 2.6.18 kernels), they pass wrong 'h' pointer down. We take IP
+ * header length from the header itself and reconstruct 'h' pointer
+ * to TCP (or whatever) header.
+ */
+ if (pBuf->h.raw == pBuf->nh.raw && pBuf->protocol == htons(ETH_P_IP))
+ pTransportHdr = pBuf->nh.raw + pBuf->nh.iph->ihl * 4;
+# endif
+ return pTransportHdr - pBuf->data;
+#endif
+}
+
+
+/**
+ * Initializes a SG list from an sk_buff.
+ *
+ * @param pThis The instance.
+ * @param pBuf The sk_buff.
+ * @param pSG The SG.
+ * @param cbExtra The number of bytes of extra space allocated immediately after the SG.
+ * @param cSegs The number of segments allocated for the SG.
+ * This should match the number in the mbuf exactly!
+ * @param fSrc The source of the frame.
+ * @param pGsoCtx Pointer to the GSO context if it's a GSO
+ * internal network frame. NULL if regular frame.
+ */
+static void vboxNetFltLinuxSkBufToSG(PVBOXNETFLTINS pThis, struct sk_buff *pBuf, PINTNETSG pSG,
+ unsigned cbExtra, unsigned cSegs, uint32_t fSrc, PCPDMNETWORKGSO pGsoCtx)
+{
+ int i;
+ NOREF(pThis);
+
+#ifndef VBOXNETFLT_SG_SUPPORT
+ Assert(!skb_shinfo(pBuf)->frag_list);
+#else /* VBOXNETFLT_SG_SUPPORT */
+ uint8_t *pExtra = (uint8_t *)&pSG->aSegs[cSegs];
+ unsigned cbConsumed = 0;
+ unsigned cbProduced = 0;
+
+# if RTLNX_VER_MIN(2,6,27)
+ /* Restore VLAN tag stripped by host hardware */
+ if (vlan_tx_tag_present(pBuf))
+ {
+ uint8_t *pMac = pBuf->data;
+ struct vlan_ethhdr *pVHdr = (struct vlan_ethhdr *)pExtra;
+ Assert(ETH_ALEN * 2 + VLAN_HLEN <= cbExtra);
+ memmove(pVHdr, pMac, ETH_ALEN * 2);
+ /* Consume whole Ethernet header: 2 addresses + EtherType (see @bugref{8599}) */
+ cbConsumed += ETH_ALEN * 2 + sizeof(uint16_t);
+ pVHdr->h_vlan_proto = RT_H2N_U16(ETH_P_8021Q);
+ pVHdr->h_vlan_TCI = RT_H2N_U16(vlan_tx_tag_get(pBuf));
+ pVHdr->h_vlan_encapsulated_proto = *(uint16_t*)(pMac + ETH_ALEN * 2);
+ cbProduced += VLAN_ETH_HLEN;
+ }
+# endif /* RTLNX_VER_MIN(2,6,27) */
+
+ if (pBuf->ip_summed == CHECKSUM_PARTIAL && pBuf->pkt_type == PACKET_OUTGOING)
+ {
+ unsigned uCsumStartOffset = vboxNetFltLinuxGetChecksumStartOffset(pBuf);
+ unsigned uCsumStoreOffset = uCsumStartOffset + VBOX_SKB_CSUM_OFFSET(pBuf) - cbConsumed;
+ Log3(("cbConsumed=%u cbProduced=%u uCsumStartOffset=%u uCsumStoreOffset=%u\n",
+ cbConsumed, cbProduced, uCsumStartOffset, uCsumStoreOffset));
+ Assert(cbProduced + uCsumStoreOffset + sizeof(uint16_t) <= cbExtra);
+ /*
+ * We assume that the checksum is stored at the very end of the transport header
+ * so we will have all headers in a single fragment. If our assumption is wrong
+ * we may see suboptimal performance.
+ */
+ memmove(pExtra + cbProduced,
+ pBuf->data + cbConsumed,
+ uCsumStoreOffset);
+ unsigned uChecksum = skb_checksum(pBuf, uCsumStartOffset, pBuf->len - uCsumStartOffset, 0);
+ *(uint16_t*)(pExtra + cbProduced + uCsumStoreOffset) = csum_fold(uChecksum);
+ cbProduced += uCsumStoreOffset + sizeof(uint16_t);
+ cbConsumed += uCsumStoreOffset + sizeof(uint16_t);
+ }
+#endif /* VBOXNETFLT_SG_SUPPORT */
+
+ if (!pGsoCtx)
+ IntNetSgInitTempSegs(pSG, pBuf->len + cbProduced - cbConsumed, cSegs, 0 /*cSegsUsed*/);
+ else
+ IntNetSgInitTempSegsGso(pSG, pBuf->len + cbProduced - cbConsumed, cSegs, 0 /*cSegsUsed*/, pGsoCtx);
+
+ int iSeg = 0;
+#ifdef VBOXNETFLT_SG_SUPPORT
+ if (cbProduced)
+ {
+ pSG->aSegs[iSeg].cb = cbProduced;
+ pSG->aSegs[iSeg].pv = pExtra;
+ pSG->aSegs[iSeg++].Phys = NIL_RTHCPHYS;
+ }
+ pSG->aSegs[iSeg].cb = skb_headlen(pBuf) - cbConsumed;
+ pSG->aSegs[iSeg].pv = pBuf->data + cbConsumed;
+ pSG->aSegs[iSeg++].Phys = NIL_RTHCPHYS;
+ Assert(iSeg <= pSG->cSegsAlloc);
+
+# ifdef LOG_ENABLED
+ if (pBuf->data_len)
+ Log6((" kmap_atomic:"));
+# endif /* LOG_ENABLED */
+ for (i = 0; i < skb_shinfo(pBuf)->nr_frags; i++)
+ {
+ skb_frag_t *pFrag = &skb_shinfo(pBuf)->frags[i];
+# if RTLNX_VER_MIN(5,4,0) || RTLNX_SUSE_MAJ_PREREQ(15, 2)
+ pSG->aSegs[iSeg].cb = pFrag->bv_len;
+ pSG->aSegs[iSeg].pv = VBOX_SKB_KMAP_FRAG(pFrag) + pFrag->bv_offset;
+# else /* < KERNEL_VERSION(5, 4, 0) */
+ pSG->aSegs[iSeg].cb = pFrag->size;
+ pSG->aSegs[iSeg].pv = VBOX_SKB_KMAP_FRAG(pFrag) + pFrag->page_offset;
+# endif /* >= KERNEL_VERSION(5, 4, 0) */
+ Log6((" %p", pSG->aSegs[iSeg].pv));
+ pSG->aSegs[iSeg++].Phys = NIL_RTHCPHYS;
+ Assert(iSeg <= pSG->cSegsAlloc);
+ }
+ struct sk_buff *pFragBuf;
+ for (pFragBuf = skb_shinfo(pBuf)->frag_list; pFragBuf; pFragBuf = pFragBuf->next)
+ {
+ pSG->aSegs[iSeg].cb = skb_headlen(pFragBuf);
+ pSG->aSegs[iSeg].pv = pFragBuf->data;
+ pSG->aSegs[iSeg++].Phys = NIL_RTHCPHYS;
+ Assert(iSeg <= pSG->cSegsAlloc);
+ for (i = 0; i < skb_shinfo(pFragBuf)->nr_frags; i++)
+ {
+ skb_frag_t *pFrag = &skb_shinfo(pFragBuf)->frags[i];
+# if RTLNX_VER_MIN(5,4,0) || RTLNX_SUSE_MAJ_PREREQ(15, 2)
+ pSG->aSegs[iSeg].cb = pFrag->bv_len;
+ pSG->aSegs[iSeg].pv = VBOX_SKB_KMAP_FRAG(pFrag) + pFrag->bv_offset;
+# else /* < KERNEL_VERSION(5, 4, 0) */
+ pSG->aSegs[iSeg].cb = pFrag->size;
+ pSG->aSegs[iSeg].pv = VBOX_SKB_KMAP_FRAG(pFrag) + pFrag->page_offset;
+# endif /* >= KERNEL_VERSION(5, 4, 0) */
+ Log6((" %p", pSG->aSegs[iSeg].pv));
+ pSG->aSegs[iSeg++].Phys = NIL_RTHCPHYS;
+ Assert(iSeg <= pSG->cSegsAlloc);
+ }
+ }
+# ifdef LOG_ENABLED
+ if (pBuf->data_len)
+ Log6(("\n"));
+# endif /* LOG_ENABLED */
+#else
+ pSG->aSegs[iSeg].cb = pBuf->len;
+ pSG->aSegs[iSeg].pv = pBuf->data;
+ pSG->aSegs[iSeg++].Phys = NIL_RTHCPHYS;
+#endif
+
+ pSG->cSegsUsed = iSeg;
+
+#if 0
+ if (cbProduced)
+ {
+ LogRel(("vboxNetFltLinuxSkBufToSG: original packet dump:\n%.*Rhxd\n", pBuf->len-pBuf->data_len, skb_mac_header(pBuf)));
+ LogRel(("vboxNetFltLinuxSkBufToSG: cbConsumed=%u cbProduced=%u cbExtra=%u\n", cbConsumed, cbProduced, cbExtra));
+ uint32_t offset = 0;
+ for (i = 0; i < pSG->cSegsUsed; ++i)
+ {
+ LogRel(("vboxNetFltLinuxSkBufToSG: seg#%d (%d bytes, starting at 0x%x):\n%.*Rhxd\n",
+ i, pSG->aSegs[i].cb, offset, pSG->aSegs[i].cb, pSG->aSegs[i].pv));
+ offset += pSG->aSegs[i].cb;
+ }
+ }
+#endif
+
+#ifdef PADD_RUNT_FRAMES_FROM_HOST
+ /*
+ * Add a trailer if the frame is too small.
+ *
+ * Since we're getting to the packet before it is framed, it has not
+ * yet been padded. The current solution is to add a segment pointing
+ * to a buffer containing all zeros and pray that works for all frames...
+ */
+ if (pSG->cbTotal < 60 && (fSrc & INTNETTRUNKDIR_HOST))
+ {
+ Assert(pBuf->data_len == 0); /* Packets with fragments are never small! */
+ static uint8_t const s_abZero[128] = {0};
+
+ AssertReturnVoid(iSeg < cSegs);
+
+ pSG->aSegs[iSeg].Phys = NIL_RTHCPHYS;
+ pSG->aSegs[iSeg].pv = (void *)&s_abZero[0];
+ pSG->aSegs[iSeg++].cb = 60 - pSG->cbTotal;
+ pSG->cbTotal = 60;
+ pSG->cSegsUsed++;
+ Assert(iSeg <= pSG->cSegsAlloc)
+ }
+#endif
+
+ Log6(("vboxNetFltLinuxSkBufToSG: allocated=%d, segments=%d frags=%d next=%p frag_list=%p pkt_type=%x fSrc=%x\n",
+ pSG->cSegsAlloc, pSG->cSegsUsed, skb_shinfo(pBuf)->nr_frags, pBuf->next, skb_shinfo(pBuf)->frag_list, pBuf->pkt_type, fSrc));
+ for (i = 0; i < pSG->cSegsUsed; i++)
+ Log6(("vboxNetFltLinuxSkBufToSG: #%d: cb=%d pv=%p\n",
+ i, pSG->aSegs[i].cb, pSG->aSegs[i].pv));
+}
+
+/**
+ * Packet handler; not really documented - figure it out yourself.
+ *
+ * @returns 0 or EJUSTRETURN - this is probably copy & pastry and thus wrong.
+ */
+#if RTLNX_VER_MIN(2,6,14)
+static int vboxNetFltLinuxPacketHandler(struct sk_buff *pBuf,
+ struct net_device *pSkbDev,
+ struct packet_type *pPacketType,
+ struct net_device *pOrigDev)
+#else
+static int vboxNetFltLinuxPacketHandler(struct sk_buff *pBuf,
+ struct net_device *pSkbDev,
+ struct packet_type *pPacketType)
+#endif
+{
+ PVBOXNETFLTINS pThis;
+ struct net_device *pDev;
+ LogFlow(("vboxNetFltLinuxPacketHandler: pBuf=%p pSkbDev=%p pPacketType=%p\n",
+ pBuf, pSkbDev, pPacketType));
+#if RTLNX_VER_MIN(2,6,18)
+ Log3(("vboxNetFltLinuxPacketHandler: skb len=%u data_len=%u truesize=%u next=%p nr_frags=%u gso_size=%u gso_seqs=%u gso_type=%x frag_list=%p pkt_type=%x\n",
+ pBuf->len, pBuf->data_len, pBuf->truesize, pBuf->next, skb_shinfo(pBuf)->nr_frags, skb_shinfo(pBuf)->gso_size, skb_shinfo(pBuf)->gso_segs, skb_shinfo(pBuf)->gso_type, skb_shinfo(pBuf)->frag_list, pBuf->pkt_type));
+# if RTLNX_VER_MIN(2,6,22)
+ Log6(("vboxNetFltLinuxPacketHandler: packet dump follows:\n%.*Rhxd\n", pBuf->len-pBuf->data_len, skb_mac_header(pBuf)));
+# endif
+#else
+ Log3(("vboxNetFltLinuxPacketHandler: skb len=%u data_len=%u truesize=%u next=%p nr_frags=%u tso_size=%u tso_seqs=%u frag_list=%p pkt_type=%x\n",
+ pBuf->len, pBuf->data_len, pBuf->truesize, pBuf->next, skb_shinfo(pBuf)->nr_frags, skb_shinfo(pBuf)->tso_size, skb_shinfo(pBuf)->tso_segs, skb_shinfo(pBuf)->frag_list, pBuf->pkt_type));
+#endif
+ /*
+ * Drop it immediately?
+ */
+ if (!pBuf)
+ return 0;
+
+ if (pBuf->pkt_type == PACKET_LOOPBACK)
+ {
+ /*
+ * We are not interested in loopbacked packets as they will always have
+ * another copy going to the wire.
+ */
+ Log2(("vboxNetFltLinuxPacketHandler: dropped loopback packet (cb=%u)\n", pBuf->len));
+ dev_kfree_skb(pBuf); /* We must 'consume' all packets we get (@bugref{6539})! */
+ return 0;
+ }
+
+ pThis = VBOX_FLT_PT_TO_INST(pPacketType);
+ pDev = ASMAtomicUoReadPtrT(&pThis->u.s.pDev, struct net_device *);
+ if (pDev != pSkbDev)
+ {
+ Log(("vboxNetFltLinuxPacketHandler: Devices do not match, pThis may be wrong! pThis=%p\n", pThis));
+ kfree_skb(pBuf); /* This is a failure, so we use kfree_skb instead of dev_kfree_skb. */
+ return 0;
+ }
+
+ Log6(("vboxNetFltLinuxPacketHandler: pBuf->cb dump:\n%.*Rhxd\n", sizeof(pBuf->cb), pBuf->cb));
+ if (vboxNetFltLinuxSkBufIsOur(pBuf))
+ {
+ Log2(("vboxNetFltLinuxPacketHandler: got our own sk_buff, drop it.\n"));
+ dev_kfree_skb(pBuf);
+ return 0;
+ }
+
+#ifndef VBOXNETFLT_SG_SUPPORT
+ {
+ /*
+ * Get rid of fragmented packets, they cause too much trouble.
+ */
+ unsigned int uMacLen = pBuf->mac_len;
+ struct sk_buff *pCopy = skb_copy(pBuf, GFP_ATOMIC);
+ dev_kfree_skb(pBuf);
+ if (!pCopy)
+ {
+ LogRel(("VBoxNetFlt: Failed to allocate packet buffer, dropping the packet.\n"));
+ return 0;
+ }
+ pBuf = pCopy;
+ /* Somehow skb_copy ignores mac_len */
+ pBuf->mac_len = uMacLen;
+# if RTLNX_VER_MIN(2,6,27)
+ /* Restore VLAN tag stripped by host hardware */
+ if (vlan_tx_tag_present(pBuf) && skb_headroom(pBuf) >= VLAN_ETH_HLEN)
+ {
+ uint8_t *pMac = (uint8_t*)skb_mac_header(pBuf);
+ struct vlan_ethhdr *pVHdr = (struct vlan_ethhdr *)(pMac - VLAN_HLEN);
+ memmove(pVHdr, pMac, ETH_ALEN * 2);
+ pVHdr->h_vlan_proto = RT_H2N_U16(ETH_P_8021Q);
+ pVHdr->h_vlan_TCI = RT_H2N_U16(vlan_tx_tag_get(pBuf));
+ pBuf->mac_header -= VLAN_HLEN;
+ pBuf->mac_len += VLAN_HLEN;
+ }
+# endif /* RTLNX_VER_MIN(2,6,27) */
+
+# if RTLNX_VER_MIN(2,6,18)
+ Log3(("vboxNetFltLinuxPacketHandler: skb copy len=%u data_len=%u truesize=%u next=%p nr_frags=%u gso_size=%u gso_seqs=%u gso_type=%x frag_list=%p pkt_type=%x\n",
+ pBuf->len, pBuf->data_len, pBuf->truesize, pBuf->next, skb_shinfo(pBuf)->nr_frags, skb_shinfo(pBuf)->gso_size, skb_shinfo(pBuf)->gso_segs, skb_shinfo(pBuf)->gso_type, skb_shinfo(pBuf)->frag_list, pBuf->pkt_type));
+# if RTLNX_VER_MIN(2,6,22)
+ Log6(("vboxNetFltLinuxPacketHandler: packet dump follows:\n%.*Rhxd\n", pBuf->len-pBuf->data_len, skb_mac_header(pBuf)));
+# endif /* RTLNX_VER_MIN(2,6,22) */
+# else /* RTLNX_VER_MAX(2,6,18) */
+ Log3(("vboxNetFltLinuxPacketHandler: skb copy len=%u data_len=%u truesize=%u next=%p nr_frags=%u tso_size=%u tso_seqs=%u frag_list=%p pkt_type=%x\n",
+ pBuf->len, pBuf->data_len, pBuf->truesize, pBuf->next, skb_shinfo(pBuf)->nr_frags, skb_shinfo(pBuf)->tso_size, skb_shinfo(pBuf)->tso_segs, skb_shinfo(pBuf)->frag_list, pBuf->pkt_type));
+# endif /* RTLNX_VER_MAX(2,6,18) */
+ }
+#endif /* !VBOXNETFLT_SG_SUPPORT */
+
+#ifdef VBOXNETFLT_LINUX_NO_XMIT_QUEUE
+ /* Forward it to the internal network. */
+ vboxNetFltLinuxForwardToIntNet(pThis, pBuf);
+#else /* !VBOXNETFLT_LINUX_NO_XMIT_QUEUE */
+ /* Add the packet to transmit queue and schedule the bottom half. */
+ skb_queue_tail(&pThis->u.s.XmitQueue, pBuf);
+ schedule_work(&pThis->u.s.XmitTask);
+ Log6(("vboxNetFltLinuxPacketHandler: scheduled work %p for sk_buff %p\n",
+ &pThis->u.s.XmitTask, pBuf));
+#endif /* !VBOXNETFLT_LINUX_NO_XMIT_QUEUE */
+
+ /* It does not really matter what we return, it is ignored by the kernel. */
+ return 0;
+}
+
+/**
+ * Calculate the number of INTNETSEG segments the socket buffer will need.
+ *
+ * @returns Segment count.
+ * @param pBuf The socket buffer.
+ * @param pcbTemp Where to store the number of bytes of the part
+ * of the socket buffer that will be copied to
+ * a temporary storage.
+ */
+DECLINLINE(unsigned) vboxNetFltLinuxCalcSGSegments(struct sk_buff *pBuf, unsigned *pcbTemp)
+{
+ *pcbTemp = 0;
+#ifdef VBOXNETFLT_SG_SUPPORT
+ unsigned cSegs = 1 + skb_shinfo(pBuf)->nr_frags;
+ if (pBuf->ip_summed == CHECKSUM_PARTIAL && pBuf->pkt_type == PACKET_OUTGOING)
+ {
+ *pcbTemp = vboxNetFltLinuxGetChecksumStartOffset(pBuf) + VBOX_SKB_CSUM_OFFSET(pBuf) + sizeof(uint16_t);
+ }
+# if RTLNX_VER_MIN(2,6,27)
+ if (vlan_tx_tag_present(pBuf))
+ {
+ if (*pcbTemp)
+ *pcbTemp += VLAN_HLEN;
+ else
+ *pcbTemp = VLAN_ETH_HLEN;
+ }
+# endif /* RTLNX_VER_MIN(2,6,27) */
+ if (*pcbTemp)
+ ++cSegs;
+ struct sk_buff *pFrag;
+ for (pFrag = skb_shinfo(pBuf)->frag_list; pFrag; pFrag = pFrag->next)
+ {
+ Log6(("vboxNetFltLinuxCalcSGSegments: frag=%p len=%d data_len=%d frags=%d frag_list=%p next=%p\n",
+ pFrag, pFrag->len, pFrag->data_len, skb_shinfo(pFrag)->nr_frags, skb_shinfo(pFrag)->frag_list, pFrag->next));
+ cSegs += 1 + skb_shinfo(pFrag)->nr_frags;
+ }
+#else
+ unsigned cSegs = 1;
+#endif
+#ifdef PADD_RUNT_FRAMES_FROM_HOST
+ /* vboxNetFltLinuxSkBufToSG adds a padding segment if it's a runt. */
+ if (pBuf->len < 60)
+ cSegs++;
+#endif
+ return cSegs;
+}
+
+
+/**
+ * Destroy the intnet scatter / gather buffer created by
+ * vboxNetFltLinuxSkBufToSG.
+ *
+ * @param pSG The (scatter/)gather list.
+ * @param pBuf The original socket buffer that was used to create
+ * the scatter/gather list.
+ */
+static void vboxNetFltLinuxDestroySG(PINTNETSG pSG, struct sk_buff *pBuf)
+{
+#ifdef VBOXNETFLT_SG_SUPPORT
+ int i, iSeg = 1; /* Skip non-paged part of SKB */
+ /* Check if the extra buffer behind SG structure was used for modified packet header */
+ if (pBuf->data != pSG->aSegs[0].pv)
+ ++iSeg; /* Skip it as well */
+# ifdef LOG_ENABLED
+ if (pBuf->data_len)
+ Log6(("kunmap_atomic:"));
+# endif /* LOG_ENABLED */
+ /* iSeg now points to the first mapped fragment if there are any */
+ for (i = 0; i < skb_shinfo(pBuf)->nr_frags; i++)
+ {
+ Log6((" %p", pSG->aSegs[iSeg].pv));
+ VBOX_SKB_KUNMAP_FRAG(pSG->aSegs[iSeg++].pv);
+ }
+ struct sk_buff *pFragBuf;
+ for (pFragBuf = skb_shinfo(pBuf)->frag_list; pFragBuf; pFragBuf = pFragBuf->next)
+ {
+ ++iSeg; /* Non-fragment (unmapped) portion of chained SKB */
+ for (i = 0; i < skb_shinfo(pFragBuf)->nr_frags; i++)
+ {
+ Log6((" %p", pSG->aSegs[iSeg].pv));
+ VBOX_SKB_KUNMAP_FRAG(pSG->aSegs[iSeg++].pv);
+ }
+ }
+# ifdef LOG_ENABLED
+ if (pBuf->data_len)
+ Log6(("\n"));
+# endif /* LOG_ENABLED */
+#endif
+ NOREF(pSG);
+}
+
+#ifdef LOG_ENABLED
+/**
+ * Logging helper.
+ */
+static void vboxNetFltDumpPacket(PINTNETSG pSG, bool fEgress, const char *pszWhere, int iIncrement)
+{
+ int i, offSeg;
+ uint8_t *pInt, *pExt;
+ static int iPacketNo = 1;
+ iPacketNo += iIncrement;
+ if (fEgress)
+ {
+ pExt = pSG->aSegs[0].pv;
+ pInt = pExt + 6;
+ }
+ else
+ {
+ pInt = pSG->aSegs[0].pv;
+ pExt = pInt + 6;
+ }
+ Log(("VBoxNetFlt: (int)%02x:%02x:%02x:%02x:%02x:%02x"
+ " %s (%s)%02x:%02x:%02x:%02x:%02x:%02x (%u bytes) packet #%u\n",
+ pInt[0], pInt[1], pInt[2], pInt[3], pInt[4], pInt[5],
+ fEgress ? "-->" : "<--", pszWhere,
+ pExt[0], pExt[1], pExt[2], pExt[3], pExt[4], pExt[5],
+ pSG->cbTotal, iPacketNo));
+ if (pSG->cSegsUsed == 1)
+ {
+ Log4(("%.*Rhxd\n", pSG->aSegs[0].cb, pSG->aSegs[0].pv));
+ }
+ else
+ {
+ for (i = 0, offSeg = 0; i < pSG->cSegsUsed; i++)
+ {
+ Log4(("-- segment %d at 0x%x (%d bytes)\n --\n%.*Rhxd\n",
+ i, offSeg, pSG->aSegs[i].cb, pSG->aSegs[i].cb, pSG->aSegs[i].pv));
+ offSeg += pSG->aSegs[i].cb;
+ }
+ }
+}
+#else
+# define vboxNetFltDumpPacket(a, b, c, d) do {} while (0)
+#endif
+
+#ifdef VBOXNETFLT_WITH_GSO_RECV
+
+/**
+ * Worker for vboxNetFltLinuxForwardToIntNet that checks if we can forwards a
+ * GSO socket buffer without having to segment it.
+ *
+ * @returns true on success, false if needs segmenting.
+ * @param pThis The net filter instance.
+ * @param pSkb The GSO socket buffer.
+ * @param fSrc The source.
+ * @param pGsoCtx Where to return the GSO context on success.
+ */
+static bool vboxNetFltLinuxCanForwardAsGso(PVBOXNETFLTINS pThis, struct sk_buff *pSkb, uint32_t fSrc,
+ PPDMNETWORKGSO pGsoCtx)
+{
+ PDMNETWORKGSOTYPE enmGsoType;
+ uint16_t uEtherType;
+ unsigned int cbTransport;
+ unsigned int offTransport;
+ unsigned int cbTransportHdr;
+ unsigned uProtocol;
+ union
+ {
+ RTNETIPV4 IPv4;
+ RTNETIPV6 IPv6;
+ RTNETTCP Tcp;
+ uint8_t ab[40];
+ uint16_t au16[40/2];
+ uint32_t au32[40/4];
+ } Buf;
+
+ /*
+ * Check the GSO properties of the socket buffer and make sure it fits.
+ */
+ /** @todo Figure out how to handle SKB_GSO_TCP_ECN! */
+ if (RT_UNLIKELY( skb_shinfo(pSkb)->gso_type & ~(SKB_GSO_DODGY | SKB_GSO_TCPV6 | SKB_GSO_TCPV4) ))
+ {
+ Log5(("vboxNetFltLinuxCanForwardAsGso: gso_type=%#x\n", skb_shinfo(pSkb)->gso_type));
+ return false;
+ }
+ if (RT_UNLIKELY( skb_shinfo(pSkb)->gso_size < 1
+ || pSkb->len > VBOX_MAX_GSO_SIZE ))
+ {
+ Log5(("vboxNetFltLinuxCanForwardAsGso: gso_size=%#x skb_len=%#x (max=%#x)\n", skb_shinfo(pSkb)->gso_size, pSkb->len, VBOX_MAX_GSO_SIZE));
+ return false;
+ }
+
+ /*
+ * Switch on the ethertype.
+ */
+ uEtherType = pSkb->protocol;
+ if ( uEtherType == RT_H2N_U16_C(RTNET_ETHERTYPE_VLAN)
+ && pSkb->mac_len == sizeof(RTNETETHERHDR) + sizeof(uint32_t))
+ {
+ uint16_t const *puEtherType = skb_header_pointer(pSkb, sizeof(RTNETETHERHDR) + sizeof(uint16_t), sizeof(uint16_t), &Buf);
+ if (puEtherType)
+ uEtherType = *puEtherType;
+ }
+ switch (uEtherType)
+ {
+ case RT_H2N_U16_C(RTNET_ETHERTYPE_IPV4):
+ {
+ unsigned int cbHdr;
+ PCRTNETIPV4 pIPv4 = (PCRTNETIPV4)skb_header_pointer(pSkb, pSkb->mac_len, sizeof(Buf.IPv4), &Buf);
+ if (RT_UNLIKELY(!pIPv4))
+ {
+ Log5(("vboxNetFltLinuxCanForwardAsGso: failed to access IPv4 hdr\n"));
+ return false;
+ }
+
+ cbHdr = pIPv4->ip_hl * 4;
+ cbTransport = RT_N2H_U16(pIPv4->ip_len);
+ if (RT_UNLIKELY( cbHdr < RTNETIPV4_MIN_LEN
+ || cbHdr > cbTransport ))
+ {
+ Log5(("vboxNetFltLinuxCanForwardAsGso: invalid IPv4 lengths: ip_hl=%u ip_len=%u\n", pIPv4->ip_hl, RT_N2H_U16(pIPv4->ip_len)));
+ return false;
+ }
+ cbTransport -= cbHdr;
+ offTransport = pSkb->mac_len + cbHdr;
+ uProtocol = pIPv4->ip_p;
+ if (uProtocol == RTNETIPV4_PROT_TCP)
+ enmGsoType = PDMNETWORKGSOTYPE_IPV4_TCP;
+ else if (uProtocol == RTNETIPV4_PROT_UDP)
+ enmGsoType = PDMNETWORKGSOTYPE_IPV4_UDP;
+ else /** @todo IPv6: 4to6 tunneling */
+ enmGsoType = PDMNETWORKGSOTYPE_INVALID;
+ break;
+ }
+
+ case RT_H2N_U16_C(RTNET_ETHERTYPE_IPV6):
+ {
+ PCRTNETIPV6 pIPv6 = (PCRTNETIPV6)skb_header_pointer(pSkb, pSkb->mac_len, sizeof(Buf.IPv6), &Buf);
+ if (RT_UNLIKELY(!pIPv6))
+ {
+ Log5(("vboxNetFltLinuxCanForwardAsGso: failed to access IPv6 hdr\n"));
+ return false;
+ }
+
+ cbTransport = RT_N2H_U16(pIPv6->ip6_plen);
+ offTransport = pSkb->mac_len + sizeof(RTNETIPV6);
+ uProtocol = pIPv6->ip6_nxt;
+ /** @todo IPv6: Dig our way out of the other headers. */
+ if (uProtocol == RTNETIPV4_PROT_TCP)
+ enmGsoType = PDMNETWORKGSOTYPE_IPV6_TCP;
+ else if (uProtocol == RTNETIPV4_PROT_UDP)
+ enmGsoType = PDMNETWORKGSOTYPE_IPV6_UDP;
+ else
+ enmGsoType = PDMNETWORKGSOTYPE_INVALID;
+ break;
+ }
+
+ default:
+ Log5(("vboxNetFltLinuxCanForwardAsGso: uEtherType=%#x\n", RT_H2N_U16(uEtherType)));
+ return false;
+ }
+
+ if (enmGsoType == PDMNETWORKGSOTYPE_INVALID)
+ {
+ Log5(("vboxNetFltLinuxCanForwardAsGso: Unsupported protocol %d\n", uProtocol));
+ return false;
+ }
+
+ if (RT_UNLIKELY( offTransport + cbTransport <= offTransport
+ || offTransport + cbTransport > pSkb->len
+ || cbTransport < (uProtocol == RTNETIPV4_PROT_TCP ? RTNETTCP_MIN_LEN : RTNETUDP_MIN_LEN)) )
+ {
+ Log5(("vboxNetFltLinuxCanForwardAsGso: Bad transport length; off=%#x + cb=%#x => %#x; skb_len=%#x (%s)\n",
+ offTransport, cbTransport, offTransport + cbTransport, pSkb->len, PDMNetGsoTypeName(enmGsoType) ));
+ return false;
+ }
+
+ /*
+ * Check the TCP/UDP bits.
+ */
+ if (uProtocol == RTNETIPV4_PROT_TCP)
+ {
+ PCRTNETTCP pTcp = (PCRTNETTCP)skb_header_pointer(pSkb, offTransport, sizeof(Buf.Tcp), &Buf);
+ if (RT_UNLIKELY(!pTcp))
+ {
+ Log5(("vboxNetFltLinuxCanForwardAsGso: failed to access TCP hdr\n"));
+ return false;
+ }
+
+ cbTransportHdr = pTcp->th_off * 4;
+ pGsoCtx->cbHdrsSeg = offTransport + cbTransportHdr;
+ if (RT_UNLIKELY( cbTransportHdr < RTNETTCP_MIN_LEN
+ || cbTransportHdr > cbTransport
+ || offTransport + cbTransportHdr >= UINT8_MAX
+ || offTransport + cbTransportHdr >= pSkb->len ))
+ {
+ Log5(("vboxNetFltLinuxCanForwardAsGso: No space for TCP header; off=%#x cb=%#x skb_len=%#x\n", offTransport, cbTransportHdr, pSkb->len));
+ return false;
+ }
+
+ }
+ else
+ {
+ Assert(uProtocol == RTNETIPV4_PROT_UDP);
+ cbTransportHdr = sizeof(RTNETUDP);
+ pGsoCtx->cbHdrsSeg = offTransport; /* Exclude UDP header */
+ if (RT_UNLIKELY( offTransport + cbTransportHdr >= UINT8_MAX
+ || offTransport + cbTransportHdr >= pSkb->len ))
+ {
+ Log5(("vboxNetFltLinuxCanForwardAsGso: No space for UDP header; off=%#x skb_len=%#x\n", offTransport, pSkb->len));
+ return false;
+ }
+ }
+
+ /*
+ * We're good, init the GSO context.
+ */
+ pGsoCtx->u8Type = enmGsoType;
+ pGsoCtx->cbHdrsTotal = offTransport + cbTransportHdr;
+ pGsoCtx->cbMaxSeg = skb_shinfo(pSkb)->gso_size;
+ pGsoCtx->offHdr1 = pSkb->mac_len;
+ pGsoCtx->offHdr2 = offTransport;
+ pGsoCtx->u8Unused = 0;
+
+ return true;
+}
+
+/**
+ * Forward the socket buffer as a GSO internal network frame.
+ *
+ * @returns IPRT status code.
+ * @param pThis The net filter instance.
+ * @param pSkb The GSO socket buffer.
+ * @param fSrc The source.
+ * @param pGsoCtx Where to return the GSO context on success.
+ */
+static int vboxNetFltLinuxForwardAsGso(PVBOXNETFLTINS pThis, struct sk_buff *pSkb, uint32_t fSrc, PCPDMNETWORKGSO pGsoCtx)
+{
+ int rc;
+ unsigned cbExtra;
+ unsigned cSegs = vboxNetFltLinuxCalcSGSegments(pSkb, &cbExtra);
+ PINTNETSG pSG = (PINTNETSG)alloca(RT_UOFFSETOF_DYN(INTNETSG, aSegs[cSegs]) + cbExtra);
+ if (RT_LIKELY(pSG))
+ {
+ vboxNetFltLinuxSkBufToSG(pThis, pSkb, pSG, cbExtra, cSegs, fSrc, pGsoCtx);
+
+ vboxNetFltDumpPacket(pSG, false, (fSrc & INTNETTRUNKDIR_HOST) ? "host" : "wire", 1);
+ pThis->pSwitchPort->pfnRecv(pThis->pSwitchPort, NULL /* pvIf */, pSG, fSrc);
+
+ vboxNetFltLinuxDestroySG(pSG, pSkb);
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ Log(("VBoxNetFlt: Dropping the sk_buff (failure case).\n"));
+ rc = VERR_NO_MEMORY;
+ }
+ return rc;
+}
+
+#endif /* VBOXNETFLT_WITH_GSO_RECV */
+
+/**
+ * Worker for vboxNetFltLinuxForwardToIntNet.
+ *
+ * @returns VINF_SUCCESS or VERR_NO_MEMORY.
+ * @param pThis The net filter instance.
+ * @param pBuf The socket buffer.
+ * @param fSrc The source.
+ */
+static int vboxNetFltLinuxForwardSegment(PVBOXNETFLTINS pThis, struct sk_buff *pBuf, uint32_t fSrc)
+{
+ int rc;
+ unsigned cbExtra;
+ unsigned cSegs = vboxNetFltLinuxCalcSGSegments(pBuf, &cbExtra);
+ PINTNETSG pSG = (PINTNETSG)alloca(RT_UOFFSETOF_DYN(INTNETSG, aSegs[cSegs]) + cbExtra);
+ if (RT_LIKELY(pSG))
+ {
+ vboxNetFltLinuxSkBufToSG(pThis, pBuf, pSG, cbExtra, cSegs, fSrc, NULL /*pGsoCtx*/);
+
+ vboxNetFltDumpPacket(pSG, false, (fSrc & INTNETTRUNKDIR_HOST) ? "host" : "wire", 1);
+ pThis->pSwitchPort->pfnRecv(pThis->pSwitchPort, NULL /* pvIf */, pSG, fSrc);
+
+ vboxNetFltLinuxDestroySG(pSG, pBuf);
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ Log(("VBoxNetFlt: Failed to allocate SG buffer.\n"));
+ rc = VERR_NO_MEMORY;
+ }
+ return rc;
+}
+
+
+/**
+ * I won't disclose what I do, figure it out yourself, including pThis referencing.
+ *
+ * @param pThis The net filter instance.
+ * @param pBuf The socket buffer.
+ * @param fSrc Where the packet comes from.
+ */
+static void vboxNetFltLinuxForwardToIntNetInner(PVBOXNETFLTINS pThis, struct sk_buff *pBuf, uint32_t fSrc)
+{
+#ifdef VBOXNETFLT_WITH_GSO
+ if (skb_is_gso(pBuf))
+ {
+ PDMNETWORKGSO GsoCtx;
+ Log6(("vboxNetFltLinuxForwardToIntNetInner: skb len=%u data_len=%u truesize=%u next=%p"
+ " nr_frags=%u gso_size=%u gso_seqs=%u gso_type=%x frag_list=%p pkt_type=%x ip_summed=%d\n",
+ pBuf->len, pBuf->data_len, pBuf->truesize, pBuf->next,
+ skb_shinfo(pBuf)->nr_frags, skb_shinfo(pBuf)->gso_size,
+ skb_shinfo(pBuf)->gso_segs, skb_shinfo(pBuf)->gso_type,
+ skb_shinfo(pBuf)->frag_list, pBuf->pkt_type, pBuf->ip_summed));
+
+ if (RT_LIKELY(fSrc & INTNETTRUNKDIR_HOST))
+ {
+ /*
+ * skb_gso_segment does the following. Do we need to do it as well?
+ */
+# if RTLNX_VER_MIN(2,6,22)
+ skb_reset_mac_header(pBuf);
+ pBuf->mac_len = pBuf->network_header - pBuf->mac_header;
+# else
+ pBuf->mac.raw = pBuf->data;
+ pBuf->mac_len = pBuf->nh.raw - pBuf->data;
+# endif
+ }
+
+# ifdef VBOXNETFLT_WITH_GSO_RECV
+ if ( (skb_shinfo(pBuf)->gso_type & (SKB_GSO_TCPV6 | SKB_GSO_TCPV4))
+ && vboxNetFltLinuxCanForwardAsGso(pThis, pBuf, fSrc, &GsoCtx) )
+ vboxNetFltLinuxForwardAsGso(pThis, pBuf, fSrc, &GsoCtx);
+ else
+# endif /* VBOXNETFLT_WITH_GSO_RECV */
+ {
+ /* Need to segment the packet */
+ struct sk_buff *pNext;
+ struct sk_buff *pSegment = skb_gso_segment(pBuf, 0 /*supported features*/);
+ if (IS_ERR(pSegment))
+ {
+ LogRel(("VBoxNetFlt: Failed to segment a packet (%d).\n", PTR_ERR(pSegment)));
+ return;
+ }
+
+ for (; pSegment; pSegment = pNext)
+ {
+ Log6(("vboxNetFltLinuxForwardToIntNetInner: segment len=%u data_len=%u truesize=%u next=%p"
+ " nr_frags=%u gso_size=%u gso_seqs=%u gso_type=%x frag_list=%p pkt_type=%x\n",
+ pSegment->len, pSegment->data_len, pSegment->truesize, pSegment->next,
+ skb_shinfo(pSegment)->nr_frags, skb_shinfo(pSegment)->gso_size,
+ skb_shinfo(pSegment)->gso_segs, skb_shinfo(pSegment)->gso_type,
+ skb_shinfo(pSegment)->frag_list, pSegment->pkt_type));
+ pNext = pSegment->next;
+ pSegment->next = 0;
+ vboxNetFltLinuxForwardSegment(pThis, pSegment, fSrc);
+ dev_kfree_skb(pSegment);
+ }
+ }
+ }
+ else
+#endif /* VBOXNETFLT_WITH_GSO */
+ {
+ Log6(("vboxNetFltLinuxForwardToIntNetInner: ptk_type=%d ip_summed=%d len=%d"
+ " data_len=%d headroom=%d hdr_len=%d csum_offset=%d\n",
+ pBuf->pkt_type, pBuf->ip_summed, pBuf->len, pBuf->data_len, skb_headroom(pBuf),
+ skb_headlen(pBuf), vboxNetFltLinuxGetChecksumStartOffset(pBuf)));
+#ifndef VBOXNETFLT_SG_SUPPORT
+ if (pBuf->ip_summed == CHECKSUM_PARTIAL && pBuf->pkt_type == PACKET_OUTGOING)
+ {
+# if RTLNX_VER_MIN(2,6,19)
+ int rc = VBOX_SKB_CHECKSUM_HELP(pBuf);
+# else
+ /*
+ * Try to work around the problem with CentOS 4.7 and 5.2 (2.6.9
+ * and 2.6.18 kernels), they pass wrong 'h' pointer down. We take IP
+ * header length from the header itself and reconstruct 'h' pointer
+ * to TCP (or whatever) header.
+ */
+ unsigned char *tmp = pBuf->h.raw;
+ if (pBuf->h.raw == pBuf->nh.raw && pBuf->protocol == htons(ETH_P_IP))
+ pBuf->h.raw = pBuf->nh.raw + pBuf->nh.iph->ihl * 4;
+ int rc = VBOX_SKB_CHECKSUM_HELP(pBuf);
+ /* Restore the original (wrong) pointer. */
+ pBuf->h.raw = tmp;
+# endif
+ if (rc)
+ {
+ LogRel(("VBoxNetFlt: Failed to compute checksum, dropping the packet.\n"));
+ return;
+ }
+ }
+#endif /* !VBOXNETFLT_SG_SUPPORT */
+ vboxNetFltLinuxForwardSegment(pThis, pBuf, fSrc);
+ }
+}
+
+
+/**
+ * Temporarily adjust pBuf->data so it always points to the Ethernet header,
+ * then forward it to the internal network.
+ *
+ * @param pThis The net filter instance.
+ * @param pBuf The socket buffer. This is consumed by this function.
+ */
+static void vboxNetFltLinuxForwardToIntNet(PVBOXNETFLTINS pThis, struct sk_buff *pBuf)
+{
+ uint32_t fSrc = pBuf->pkt_type == PACKET_OUTGOING ? INTNETTRUNKDIR_HOST : INTNETTRUNKDIR_WIRE;
+
+ if (RT_UNLIKELY(fSrc & INTNETTRUNKDIR_WIRE))
+ {
+ /*
+ * The packet came from the wire and the driver has already consumed
+ * mac header. We need to restore it back. Moreover, after we are
+ * through with this skb we need to restore its original state!
+ */
+ skb_push(pBuf, pBuf->mac_len);
+ Log5(("vboxNetFltLinuxForwardToIntNet: mac_len=%d data=%p mac_header=%p network_header=%p\n",
+ pBuf->mac_len, pBuf->data, skb_mac_header(pBuf), skb_network_header(pBuf)));
+ }
+
+ vboxNetFltLinuxForwardToIntNetInner(pThis, pBuf, fSrc);
+
+ /*
+ * Restore the original state of skb as there are other handlers this skb
+ * will be provided to.
+ */
+ if (RT_UNLIKELY(fSrc & INTNETTRUNKDIR_WIRE))
+ skb_pull(pBuf, pBuf->mac_len);
+
+ dev_kfree_skb(pBuf);
+}
+
+
+#ifndef VBOXNETFLT_LINUX_NO_XMIT_QUEUE
+/**
+ * Work queue handler that forwards the socket buffers queued by
+ * vboxNetFltLinuxPacketHandler to the internal network.
+ *
+ * @param pWork The work queue.
+ */
+# if RTLNX_VER_MIN(2,6,20)
+static void vboxNetFltLinuxXmitTask(struct work_struct *pWork)
+# else
+static void vboxNetFltLinuxXmitTask(void *pWork)
+# endif
+{
+ PVBOXNETFLTINS pThis = VBOX_FLT_XT_TO_INST(pWork);
+ struct sk_buff *pBuf;
+
+ Log6(("vboxNetFltLinuxXmitTask: Got work %p.\n", pWork));
+
+ /*
+ * Active? Retain the instance and increment the busy counter.
+ */
+ if (vboxNetFltTryRetainBusyActive(pThis))
+ {
+ while ((pBuf = skb_dequeue(&pThis->u.s.XmitQueue)) != NULL)
+ vboxNetFltLinuxForwardToIntNet(pThis, pBuf);
+
+ vboxNetFltRelease(pThis, true /* fBusy */);
+ }
+ else
+ {
+ /** @todo Shouldn't we just drop the packets here? There is little point in
+ * making them accumulate when the VM is paused and it'll only waste
+ * kernel memory anyway... Hmm. maybe wait a short while (2-5 secs)
+ * before start draining the packets (goes for the intnet ring buf
+ * too)? */
+ }
+}
+#endif /* !VBOXNETFLT_LINUX_NO_XMIT_QUEUE */
+
+/**
+ * Reports the GSO capabilities of the hardware NIC.
+ *
+ * @param pThis The net filter instance. The caller hold a
+ * reference to this.
+ */
+static void vboxNetFltLinuxReportNicGsoCapabilities(PVBOXNETFLTINS pThis)
+{
+#if defined(VBOXNETFLT_WITH_GSO_XMIT_WIRE) || defined(VBOXNETFLT_WITH_GSO_XMIT_HOST)
+ if (vboxNetFltTryRetainBusyNotDisconnected(pThis))
+ {
+ struct net_device *pDev;
+ unsigned int fFeatures;
+
+ RTSpinlockAcquire(pThis->hSpinlock);
+
+ pDev = ASMAtomicUoReadPtrT(&pThis->u.s.pDev, struct net_device *);
+ if (pDev)
+ fFeatures = pDev->features;
+ else
+ fFeatures = 0;
+
+ RTSpinlockRelease(pThis->hSpinlock);
+
+ if (pThis->pSwitchPort)
+ {
+ /* Set/update the GSO capabilities of the NIC. */
+ uint32_t fGsoCapabilites = 0;
+ if (fFeatures & NETIF_F_TSO)
+ fGsoCapabilites |= RT_BIT_32(PDMNETWORKGSOTYPE_IPV4_TCP);
+ if (fFeatures & NETIF_F_TSO6)
+ fGsoCapabilites |= RT_BIT_32(PDMNETWORKGSOTYPE_IPV6_TCP);
+ Log3(("vboxNetFltLinuxReportNicGsoCapabilities: reporting wire %s%s\n",
+ (fGsoCapabilites & RT_BIT_32(PDMNETWORKGSOTYPE_IPV4_TCP)) ? "tso " : "",
+ (fGsoCapabilites & RT_BIT_32(PDMNETWORKGSOTYPE_IPV6_TCP)) ? "tso6 " : ""));
+ pThis->pSwitchPort->pfnReportGsoCapabilities(pThis->pSwitchPort, fGsoCapabilites, INTNETTRUNKDIR_WIRE);
+ }
+
+ vboxNetFltRelease(pThis, true /*fBusy*/);
+ }
+#endif /* VBOXNETFLT_WITH_GSO_XMIT_WIRE || VBOXNETFLT_WITH_GSO_XMIT_HOST */
+}
+
+/**
+ * Helper that determines whether the host (ignoreing us) is operating the
+ * interface in promiscuous mode or not.
+ */
+static bool vboxNetFltLinuxPromiscuous(PVBOXNETFLTINS pThis)
+{
+ bool fRc = false;
+ struct net_device * pDev = vboxNetFltLinuxRetainNetDev(pThis);
+ if (pDev)
+ {
+ fRc = !!(pDev->promiscuity - (ASMAtomicUoReadBool(&pThis->u.s.fPromiscuousSet) & 1));
+ LogFlow(("vboxNetFltPortOsIsPromiscuous: returns %d, pDev->promiscuity=%d, fPromiscuousSet=%d\n",
+ fRc, pDev->promiscuity, pThis->u.s.fPromiscuousSet));
+ vboxNetFltLinuxReleaseNetDev(pThis, pDev);
+ }
+ return fRc;
+}
+
+/**
+ * Does this device needs link state change signaled?
+ * Currently we need it for our own VBoxNetAdp and TAP.
+ */
+static bool vboxNetFltNeedsLinkState(PVBOXNETFLTINS pThis, struct net_device *pDev)
+{
+ if (pDev->ethtool_ops && pDev->ethtool_ops->get_drvinfo)
+ {
+ struct ethtool_drvinfo Info;
+
+ memset(&Info, 0, sizeof(Info));
+ Info.cmd = ETHTOOL_GDRVINFO;
+ pDev->ethtool_ops->get_drvinfo(pDev, &Info);
+ Log3(("%s: driver=%.*s version=%.*s bus_info=%.*s\n",
+ __FUNCTION__,
+ sizeof(Info.driver), Info.driver,
+ sizeof(Info.version), Info.version,
+ sizeof(Info.bus_info), Info.bus_info));
+
+ if (!strncmp(Info.driver, "vboxnet", sizeof(Info.driver)))
+ return true;
+
+#if RTLNX_VER_MIN(2,6,36) /* TAP started doing carrier */
+ return !strncmp(Info.driver, "tun", 4)
+ && !strncmp(Info.bus_info, "tap", 4);
+#endif
+ }
+
+ return false;
+}
+
+#if RTLNX_VER_MAX(2,6,18)
+DECLINLINE(void) netif_tx_lock_bh(struct net_device *pDev)
+{
+ spin_lock_bh(&pDev->xmit_lock);
+}
+
+DECLINLINE(void) netif_tx_unlock_bh(struct net_device *pDev)
+{
+ spin_unlock_bh(&pDev->xmit_lock);
+}
+#endif
+
+/**
+ * Some devices need link state change when filter attaches/detaches
+ * since the filter is their link in a sense.
+ */
+static void vboxNetFltSetLinkState(PVBOXNETFLTINS pThis, struct net_device *pDev, bool fLinkUp)
+{
+ if (vboxNetFltNeedsLinkState(pThis, pDev))
+ {
+ Log3(("%s: bringing device link %s\n",
+ __FUNCTION__, fLinkUp ? "up" : "down"));
+ netif_tx_lock_bh(pDev);
+ if (fLinkUp)
+ netif_carrier_on(pDev);
+ else
+ netif_carrier_off(pDev);
+ netif_tx_unlock_bh(pDev);
+ }
+}
+
+/**
+ * Internal worker for vboxNetFltLinuxNotifierCallback.
+ *
+ * @returns VBox status code.
+ * @param pThis The instance.
+ * @param pDev The device to attach to.
+ */
+static int vboxNetFltLinuxAttachToInterface(PVBOXNETFLTINS pThis, struct net_device *pDev)
+{
+ LogFlow(("vboxNetFltLinuxAttachToInterface: pThis=%p (%s)\n", pThis, pThis->szName));
+
+ /*
+ * Retain and store the device.
+ */
+ dev_hold(pDev);
+
+ RTSpinlockAcquire(pThis->hSpinlock);
+ ASMAtomicUoWritePtr(&pThis->u.s.pDev, pDev);
+ RTSpinlockRelease(pThis->hSpinlock);
+
+ Log(("vboxNetFltLinuxAttachToInterface: Device %p(%s) retained. ref=%d\n",
+ pDev, pDev->name,
+#if RTLNX_VER_MIN(2,6,37)
+ netdev_refcnt_read(pDev)
+#else
+ atomic_read(&pDev->refcnt)
+#endif
+ ));
+ Log(("vboxNetFltLinuxAttachToInterface: Got pDev=%p pThis=%p pThis->u.s.pDev=%p\n",
+ pDev, pThis, ASMAtomicUoReadPtrT(&pThis->u.s.pDev, struct net_device *)));
+
+ /* Get the mac address while we still have a valid net_device reference. */
+ memcpy(&pThis->u.s.MacAddr, pDev->dev_addr, sizeof(pThis->u.s.MacAddr));
+ /* Initialize MTU */
+ pThis->u.s.cbMtu = pDev->mtu;
+
+ /*
+ * Install a packet filter for this device with a protocol wildcard (ETH_P_ALL).
+ */
+ pThis->u.s.PacketType.type = __constant_htons(ETH_P_ALL);
+ pThis->u.s.PacketType.dev = pDev;
+ pThis->u.s.PacketType.func = vboxNetFltLinuxPacketHandler;
+ dev_add_pack(&pThis->u.s.PacketType);
+ ASMAtomicUoWriteBool(&pThis->u.s.fPacketHandler, true);
+ Log(("vboxNetFltLinuxAttachToInterface: this=%p: Packet handler installed.\n", pThis));
+
+#ifdef VBOXNETFLT_WITH_HOST2WIRE_FILTER
+ vboxNetFltLinuxHookDev(pThis, pDev);
+#endif
+
+ /*
+ * Are we the "carrier" for this device (e.g. vboxnet or tap)?
+ */
+ vboxNetFltSetLinkState(pThis, pDev, true);
+
+ /*
+ * Set indicators that require the spinlock. Be abit paranoid about racing
+ * the device notification handle.
+ */
+ RTSpinlockAcquire(pThis->hSpinlock);
+ pDev = ASMAtomicUoReadPtrT(&pThis->u.s.pDev, struct net_device *);
+ if (pDev)
+ {
+ ASMAtomicUoWriteBool(&pThis->fDisconnectedFromHost, false);
+ ASMAtomicUoWriteBool(&pThis->u.s.fRegistered, true);
+ pDev = NULL; /* don't dereference it */
+ }
+ RTSpinlockRelease(pThis->hSpinlock);
+
+ /*
+ * Report GSO capabilities
+ */
+ Assert(pThis->pSwitchPort);
+ if (vboxNetFltTryRetainBusyNotDisconnected(pThis))
+ {
+ vboxNetFltLinuxReportNicGsoCapabilities(pThis);
+ pThis->pSwitchPort->pfnReportMacAddress(pThis->pSwitchPort, &pThis->u.s.MacAddr);
+ pThis->pSwitchPort->pfnReportPromiscuousMode(pThis->pSwitchPort, vboxNetFltLinuxPromiscuous(pThis));
+ pThis->pSwitchPort->pfnReportNoPreemptDsts(pThis->pSwitchPort, INTNETTRUNKDIR_WIRE | INTNETTRUNKDIR_HOST);
+ vboxNetFltRelease(pThis, true /*fBusy*/);
+ }
+
+ LogRel(("VBoxNetFlt: attached to '%s' / %RTmac\n", pThis->szName, &pThis->u.s.MacAddr));
+ return VINF_SUCCESS;
+}
+
+
+static int vboxNetFltLinuxUnregisterDevice(PVBOXNETFLTINS pThis, struct net_device *pDev)
+{
+ bool fRegistered;
+ Assert(!pThis->fDisconnectedFromHost);
+
+#ifdef VBOXNETFLT_WITH_HOST2WIRE_FILTER
+ vboxNetFltLinuxUnhookDev(pThis, pDev);
+#endif
+
+ if (ASMAtomicCmpXchgBool(&pThis->u.s.fPacketHandler, false, true))
+ {
+ dev_remove_pack(&pThis->u.s.PacketType);
+ Log(("vboxNetFltLinuxUnregisterDevice: this=%p: packet handler removed.\n", pThis));
+ }
+
+ RTSpinlockAcquire(pThis->hSpinlock);
+ fRegistered = ASMAtomicXchgBool(&pThis->u.s.fRegistered, false);
+ if (fRegistered)
+ {
+ ASMAtomicWriteBool(&pThis->fDisconnectedFromHost, true);
+ ASMAtomicUoWriteNullPtr(&pThis->u.s.pDev);
+ }
+ RTSpinlockRelease(pThis->hSpinlock);
+
+ if (fRegistered)
+ {
+#ifndef VBOXNETFLT_LINUX_NO_XMIT_QUEUE
+ skb_queue_purge(&pThis->u.s.XmitQueue);
+#endif
+ Log(("vboxNetFltLinuxUnregisterDevice: this=%p: xmit queue purged.\n", pThis));
+ Log(("vboxNetFltLinuxUnregisterDevice: Device %p(%s) released. ref=%d\n",
+ pDev, pDev->name,
+#if RTLNX_VER_MIN(2,6,37)
+ netdev_refcnt_read(pDev)
+#else
+ atomic_read(&pDev->refcnt)
+#endif
+ ));
+ dev_put(pDev);
+ }
+
+ return NOTIFY_OK;
+}
+
+static int vboxNetFltLinuxDeviceIsUp(PVBOXNETFLTINS pThis, struct net_device *pDev)
+{
+ /* Check if we are not suspended and promiscuous mode has not been set. */
+ if ( pThis->enmTrunkState == INTNETTRUNKIFSTATE_ACTIVE
+ && !ASMAtomicUoReadBool(&pThis->u.s.fPromiscuousSet))
+ {
+ /* Note that there is no need for locking as the kernel got hold of the lock already. */
+ dev_set_promiscuity(pDev, 1);
+ ASMAtomicWriteBool(&pThis->u.s.fPromiscuousSet, true);
+ Log(("vboxNetFltLinuxDeviceIsUp: enabled promiscuous mode on %s (%d)\n", pThis->szName, pDev->promiscuity));
+ }
+ else
+ Log(("vboxNetFltLinuxDeviceIsUp: no need to enable promiscuous mode on %s (%d)\n", pThis->szName, pDev->promiscuity));
+ return NOTIFY_OK;
+}
+
+static int vboxNetFltLinuxDeviceGoingDown(PVBOXNETFLTINS pThis, struct net_device *pDev)
+{
+ /* Undo promiscuous mode if we has set it. */
+ if (ASMAtomicUoReadBool(&pThis->u.s.fPromiscuousSet))
+ {
+ /* Note that there is no need for locking as the kernel got hold of the lock already. */
+ dev_set_promiscuity(pDev, -1);
+ ASMAtomicWriteBool(&pThis->u.s.fPromiscuousSet, false);
+ Log(("vboxNetFltLinuxDeviceGoingDown: disabled promiscuous mode on %s (%d)\n", pThis->szName, pDev->promiscuity));
+ }
+ else
+ Log(("vboxNetFltLinuxDeviceGoingDown: no need to disable promiscuous mode on %s (%d)\n", pThis->szName, pDev->promiscuity));
+ return NOTIFY_OK;
+}
+
+/**
+ * Callback for listening to MTU change event.
+ *
+ * We need to track changes of host's inteface MTU to discard over-sized frames
+ * coming from the internal network as they may hang the TX queue of host's
+ * adapter.
+ *
+ * @returns NOTIFY_OK
+ * @param pThis The netfilter instance.
+ * @param pDev Pointer to device structure of host's interface.
+ */
+static int vboxNetFltLinuxDeviceMtuChange(PVBOXNETFLTINS pThis, struct net_device *pDev)
+{
+ ASMAtomicWriteU32(&pThis->u.s.cbMtu, pDev->mtu);
+ Log(("vboxNetFltLinuxDeviceMtuChange: set MTU for %s to %d\n", pThis->szName, pDev->mtu));
+ return NOTIFY_OK;
+}
+
+#ifdef LOG_ENABLED
+/** Stringify the NETDEV_XXX constants. */
+static const char *vboxNetFltLinuxGetNetDevEventName(unsigned long ulEventType)
+{
+ const char *pszEvent = "NETDEV_<unknown>";
+ switch (ulEventType)
+ {
+ case NETDEV_REGISTER: pszEvent = "NETDEV_REGISTER"; break;
+ case NETDEV_UNREGISTER: pszEvent = "NETDEV_UNREGISTER"; break;
+ case NETDEV_UP: pszEvent = "NETDEV_UP"; break;
+ case NETDEV_DOWN: pszEvent = "NETDEV_DOWN"; break;
+ case NETDEV_REBOOT: pszEvent = "NETDEV_REBOOT"; break;
+ case NETDEV_CHANGENAME: pszEvent = "NETDEV_CHANGENAME"; break;
+ case NETDEV_CHANGE: pszEvent = "NETDEV_CHANGE"; break;
+ case NETDEV_CHANGEMTU: pszEvent = "NETDEV_CHANGEMTU"; break;
+ case NETDEV_CHANGEADDR: pszEvent = "NETDEV_CHANGEADDR"; break;
+ case NETDEV_GOING_DOWN: pszEvent = "NETDEV_GOING_DOWN"; break;
+# ifdef NETDEV_FEAT_CHANGE
+ case NETDEV_FEAT_CHANGE: pszEvent = "NETDEV_FEAT_CHANGE"; break;
+# endif
+ }
+ return pszEvent;
+}
+#endif /* LOG_ENABLED */
+
+/**
+ * Callback for listening to netdevice events.
+ *
+ * This works the rediscovery, clean up on unregistration, promiscuity on
+ * up/down, and GSO feature changes from ethtool.
+ *
+ * @returns NOTIFY_OK
+ * @param self Pointer to our notifier registration block.
+ * @param ulEventType The event.
+ * @param ptr Event specific, but it is usually the device it
+ * relates to.
+ */
+static int vboxNetFltLinuxNotifierCallback(struct notifier_block *self, unsigned long ulEventType, void *ptr)
+
+{
+ PVBOXNETFLTINS pThis = VBOX_FLT_NB_TO_INST(self);
+ struct net_device *pMyDev = ASMAtomicUoReadPtrT(&pThis->u.s.pDev, struct net_device *);
+ struct net_device *pDev = VBOX_NETDEV_NOTIFIER_INFO_TO_DEV(ptr);
+ int rc = NOTIFY_OK;
+
+ Log(("VBoxNetFlt: got event %s(0x%lx) on %s, pDev=%p pThis=%p pThis->u.s.pDev=%p\n",
+ vboxNetFltLinuxGetNetDevEventName(ulEventType), ulEventType, pDev->name, pDev, pThis, pMyDev));
+
+ if (ulEventType == NETDEV_REGISTER)
+ {
+#if RTLNX_VER_MIN(2,6,24) /* cgroups/namespaces introduced */
+# if RTLNX_VER_MIN(2,6,26)
+# define VBOX_DEV_NET(dev) dev_net(dev)
+# define VBOX_NET_EQ(n1, n2) net_eq((n1), (n2))
+# else
+# define VBOX_DEV_NET(dev) ((dev)->nd_net)
+# define VBOX_NET_EQ(n1, n2) ((n1) == (n2))
+# endif
+ struct net *pMyNet = current->nsproxy->net_ns;
+ struct net *pDevNet = VBOX_DEV_NET(pDev);
+
+ if (VBOX_NET_EQ(pDevNet, pMyNet))
+#endif /* namespaces */
+ {
+ if (strcmp(pDev->name, pThis->szName) == 0)
+ {
+ vboxNetFltLinuxAttachToInterface(pThis, pDev);
+ }
+ }
+ }
+ else
+ {
+ if (pDev == pMyDev)
+ {
+ switch (ulEventType)
+ {
+ case NETDEV_UNREGISTER:
+ rc = vboxNetFltLinuxUnregisterDevice(pThis, pDev);
+ break;
+ case NETDEV_UP:
+ rc = vboxNetFltLinuxDeviceIsUp(pThis, pDev);
+ break;
+ case NETDEV_GOING_DOWN:
+ rc = vboxNetFltLinuxDeviceGoingDown(pThis, pDev);
+ break;
+ case NETDEV_CHANGEMTU:
+ rc = vboxNetFltLinuxDeviceMtuChange(pThis, pDev);
+ break;
+ case NETDEV_CHANGENAME:
+ break;
+#ifdef NETDEV_FEAT_CHANGE
+ case NETDEV_FEAT_CHANGE:
+ vboxNetFltLinuxReportNicGsoCapabilities(pThis);
+ break;
+#endif
+ }
+ }
+ }
+
+ return rc;
+}
+
+/*
+ * Initial enumeration of netdevs. Called with NETDEV_REGISTER by
+ * register_netdevice_notifier() under rtnl lock.
+ */
+static int vboxNetFltLinuxEnumeratorCallback(struct notifier_block *self, unsigned long ulEventType, void *ptr)
+{
+ PVBOXNETFLTINS pThis = ((PVBOXNETFLTNOTIFIER)self)->pThis;
+ struct net_device *dev = VBOX_NETDEV_NOTIFIER_INFO_TO_DEV(ptr);
+ struct in_device *in_dev;
+ struct inet6_dev *in6_dev;
+
+ if (ulEventType != NETDEV_REGISTER)
+ return NOTIFY_OK;
+
+ if (RT_UNLIKELY(pThis->pSwitchPort->pfnNotifyHostAddress == NULL))
+ return NOTIFY_OK;
+
+ /*
+ * IPv4
+ */
+#if RTLNX_VER_MIN(2,6,14)
+ in_dev = __in_dev_get_rtnl(dev);
+#else
+ in_dev = __in_dev_get(dev);
+#endif
+ if (in_dev != NULL)
+ {
+ struct in_ifaddr *ifa;
+
+ for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
+ if (VBOX_IPV4_IS_LOOPBACK(ifa->ifa_address))
+ return NOTIFY_OK;
+
+ if ( dev != pThis->u.s.pDev
+ && VBOX_IPV4_IS_LINKLOCAL_169(ifa->ifa_address))
+ continue;
+
+ Log(("%s: %s: IPv4 addr %RTnaipv4 mask %RTnaipv4\n",
+ __FUNCTION__, VBOX_NETDEV_NAME(dev),
+ ifa->ifa_address, ifa->ifa_mask));
+
+ pThis->pSwitchPort->pfnNotifyHostAddress(pThis->pSwitchPort,
+ /* :fAdded */ true, kIntNetAddrType_IPv4, &ifa->ifa_address);
+ }
+ }
+
+ /*
+ * IPv6
+ */
+ in6_dev = __in6_dev_get(dev);
+ if (in6_dev != NULL)
+ {
+ struct inet6_ifaddr *ifa;
+
+ read_lock_bh(&in6_dev->lock);
+#if RTLNX_VER_MIN(2,6,35)
+ list_for_each_entry(ifa, &in6_dev->addr_list, if_list)
+#else
+ for (ifa = in6_dev->addr_list; ifa != NULL; ifa = ifa->if_next)
+#endif
+ {
+ if ( dev != pThis->u.s.pDev
+ && ipv6_addr_type(&ifa->addr) & (IPV6_ADDR_LINKLOCAL | IPV6_ADDR_LOOPBACK))
+ continue;
+
+ Log(("%s: %s: IPv6 addr %RTnaipv6/%u\n",
+ __FUNCTION__, VBOX_NETDEV_NAME(dev),
+ &ifa->addr, (unsigned)ifa->prefix_len));
+
+ pThis->pSwitchPort->pfnNotifyHostAddress(pThis->pSwitchPort,
+ /* :fAdded */ true, kIntNetAddrType_IPv6, &ifa->addr);
+ }
+ read_unlock_bh(&in6_dev->lock);
+ }
+
+ return NOTIFY_OK;
+}
+
+
+static int vboxNetFltLinuxNotifierIPv4Callback(struct notifier_block *self, unsigned long ulEventType, void *ptr)
+{
+ PVBOXNETFLTINS pThis = RT_FROM_MEMBER(self, VBOXNETFLTINS, u.s.NotifierIPv4);
+ struct net_device *pDev, *pEventDev;
+ struct in_ifaddr *ifa = (struct in_ifaddr *)ptr;
+ bool fMyDev;
+ int rc = NOTIFY_OK;
+
+ pDev = vboxNetFltLinuxRetainNetDev(pThis);
+ pEventDev = ifa->ifa_dev->dev;
+ fMyDev = (pDev == pEventDev);
+ Log(("VBoxNetFlt: %s: IPv4 event %s(0x%lx) %s: addr %RTnaipv4 mask %RTnaipv4\n",
+ pDev ? VBOX_NETDEV_NAME(pDev) : "<unknown>",
+ vboxNetFltLinuxGetNetDevEventName(ulEventType), ulEventType,
+ pEventDev ? VBOX_NETDEV_NAME(pEventDev) : "<unknown>",
+ ifa->ifa_address, ifa->ifa_mask));
+
+ if (pDev != NULL)
+ vboxNetFltLinuxReleaseNetDev(pThis, pDev);
+
+ if (VBOX_IPV4_IS_LOOPBACK(ifa->ifa_address))
+ return NOTIFY_OK;
+
+ if ( !fMyDev
+ && VBOX_IPV4_IS_LINKLOCAL_169(ifa->ifa_address))
+ return NOTIFY_OK;
+
+ if (pThis->pSwitchPort->pfnNotifyHostAddress)
+ {
+ bool fAdded;
+ if (ulEventType == NETDEV_UP)
+ fAdded = true;
+ else if (ulEventType == NETDEV_DOWN)
+ fAdded = false;
+ else
+ return NOTIFY_OK;
+
+ pThis->pSwitchPort->pfnNotifyHostAddress(pThis->pSwitchPort, fAdded,
+ kIntNetAddrType_IPv4, &ifa->ifa_local);
+ }
+
+ return rc;
+}
+
+
+static int vboxNetFltLinuxNotifierIPv6Callback(struct notifier_block *self, unsigned long ulEventType, void *ptr)
+{
+ PVBOXNETFLTINS pThis = RT_FROM_MEMBER(self, VBOXNETFLTINS, u.s.NotifierIPv6);
+ struct net_device *pDev, *pEventDev;
+ struct inet6_ifaddr *ifa = (struct inet6_ifaddr *)ptr;
+ bool fMyDev;
+ int rc = NOTIFY_OK;
+
+ pDev = vboxNetFltLinuxRetainNetDev(pThis);
+ pEventDev = ifa->idev->dev;
+ fMyDev = (pDev == pEventDev);
+ Log(("VBoxNetFlt: %s: IPv6 event %s(0x%lx) %s: %RTnaipv6\n",
+ pDev ? VBOX_NETDEV_NAME(pDev) : "<unknown>",
+ vboxNetFltLinuxGetNetDevEventName(ulEventType), ulEventType,
+ pEventDev ? VBOX_NETDEV_NAME(pEventDev) : "<unknown>",
+ &ifa->addr));
+
+ if (pDev != NULL)
+ vboxNetFltLinuxReleaseNetDev(pThis, pDev);
+
+ if ( !fMyDev
+ && ipv6_addr_type(&ifa->addr) & (IPV6_ADDR_LINKLOCAL | IPV6_ADDR_LOOPBACK))
+ return NOTIFY_OK;
+
+ if (pThis->pSwitchPort->pfnNotifyHostAddress)
+ {
+ bool fAdded;
+ if (ulEventType == NETDEV_UP)
+ fAdded = true;
+ else if (ulEventType == NETDEV_DOWN)
+ fAdded = false;
+ else
+ return NOTIFY_OK;
+
+ pThis->pSwitchPort->pfnNotifyHostAddress(pThis->pSwitchPort, fAdded,
+ kIntNetAddrType_IPv6, &ifa->addr);
+ }
+
+ return rc;
+}
+
+
+bool vboxNetFltOsMaybeRediscovered(PVBOXNETFLTINS pThis)
+{
+ return !ASMAtomicUoReadBool(&pThis->fDisconnectedFromHost);
+}
+
+int vboxNetFltPortOsXmit(PVBOXNETFLTINS pThis, void *pvIfData, PINTNETSG pSG, uint32_t fDst)
+{
+ struct net_device * pDev;
+ int err;
+ int rc = VINF_SUCCESS;
+ IPRT_LINUX_SAVE_EFL_AC();
+ NOREF(pvIfData);
+
+ LogFlow(("vboxNetFltPortOsXmit: pThis=%p (%s)\n", pThis, pThis->szName));
+
+ pDev = vboxNetFltLinuxRetainNetDev(pThis);
+ if (pDev)
+ {
+ /*
+ * Create a sk_buff for the gather list and push it onto the wire.
+ */
+ if (fDst & INTNETTRUNKDIR_WIRE)
+ {
+ struct sk_buff *pBuf = vboxNetFltLinuxSkBufFromSG(pThis, pSG, true);
+ if (pBuf)
+ {
+ vboxNetFltDumpPacket(pSG, true, "wire", 1);
+ Log6(("vboxNetFltPortOsXmit: pBuf->cb dump:\n%.*Rhxd\n", sizeof(pBuf->cb), pBuf->cb));
+ Log6(("vboxNetFltPortOsXmit: dev_queue_xmit(%p)\n", pBuf));
+ err = dev_queue_xmit(pBuf);
+ if (err)
+ rc = RTErrConvertFromErrno(err);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+
+ /*
+ * Create a sk_buff for the gather list and push it onto the host stack.
+ */
+ if (fDst & INTNETTRUNKDIR_HOST)
+ {
+ struct sk_buff *pBuf = vboxNetFltLinuxSkBufFromSG(pThis, pSG, false);
+ if (pBuf)
+ {
+ vboxNetFltDumpPacket(pSG, true, "host", (fDst & INTNETTRUNKDIR_WIRE) ? 0 : 1);
+ Log6(("vboxNetFltPortOsXmit: pBuf->cb dump:\n%.*Rhxd\n", sizeof(pBuf->cb), pBuf->cb));
+ Log6(("vboxNetFltPortOsXmit: netif_rx_ni(%p)\n", pBuf));
+#if RTLNX_VER_MIN(5,18,0) || RTLNX_RHEL_MIN(9,1)
+ local_bh_disable();
+ err = netif_rx(pBuf);
+ local_bh_enable();
+#else
+ err = netif_rx_ni(pBuf);
+#endif
+ if (err)
+ rc = RTErrConvertFromErrno(err);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+
+ vboxNetFltLinuxReleaseNetDev(pThis, pDev);
+ }
+
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return rc;
+}
+
+
+void vboxNetFltPortOsSetActive(PVBOXNETFLTINS pThis, bool fActive)
+{
+ struct net_device *pDev;
+ IPRT_LINUX_SAVE_EFL_AC();
+
+ LogFlow(("vboxNetFltPortOsSetActive: pThis=%p (%s), fActive=%RTbool, fDisablePromiscuous=%RTbool\n",
+ pThis, pThis->szName, fActive, pThis->fDisablePromiscuous));
+
+ if (pThis->fDisablePromiscuous)
+ return;
+
+ pDev = vboxNetFltLinuxRetainNetDev(pThis);
+ if (pDev)
+ {
+ /*
+ * This api is a bit weird, the best reference is the code.
+ *
+ * Also, we have a bit or race conditions wrt the maintenance of
+ * host the interface promiscuity for vboxNetFltPortOsIsPromiscuous.
+ */
+#ifdef LOG_ENABLED
+ u_int16_t fIf;
+ unsigned const cPromiscBefore = pDev->promiscuity;
+#endif
+ if (fActive)
+ {
+ Assert(!pThis->u.s.fPromiscuousSet);
+
+ rtnl_lock();
+ dev_set_promiscuity(pDev, 1);
+ rtnl_unlock();
+ pThis->u.s.fPromiscuousSet = true;
+ Log(("vboxNetFltPortOsSetActive: enabled promiscuous mode on %s (%d)\n", pThis->szName, pDev->promiscuity));
+ }
+ else
+ {
+ if (pThis->u.s.fPromiscuousSet)
+ {
+ rtnl_lock();
+ dev_set_promiscuity(pDev, -1);
+ rtnl_unlock();
+ Log(("vboxNetFltPortOsSetActive: disabled promiscuous mode on %s (%d)\n", pThis->szName, pDev->promiscuity));
+ }
+ pThis->u.s.fPromiscuousSet = false;
+
+#ifdef LOG_ENABLED
+ fIf = dev_get_flags(pDev);
+ Log(("VBoxNetFlt: fIf=%#x; %d->%d\n", fIf, cPromiscBefore, pDev->promiscuity));
+#endif
+ }
+
+ vboxNetFltLinuxReleaseNetDev(pThis, pDev);
+ }
+ IPRT_LINUX_RESTORE_EFL_AC();
+}
+
+
+int vboxNetFltOsDisconnectIt(PVBOXNETFLTINS pThis)
+{
+ /*
+ * Remove packet handler when we get disconnected from internal switch as
+ * we don't want the handler to forward packets to disconnected switch.
+ */
+ if (ASMAtomicCmpXchgBool(&pThis->u.s.fPacketHandler, false, true))
+ {
+ IPRT_LINUX_SAVE_EFL_AC();
+ dev_remove_pack(&pThis->u.s.PacketType);
+ Log(("vboxNetFltOsDisconnectIt: this=%p: Packet handler removed.\n", pThis));
+ IPRT_LINUX_RESTORE_EFL_AC();
+ }
+ return VINF_SUCCESS;
+}
+
+
+int vboxNetFltOsConnectIt(PVBOXNETFLTINS pThis)
+{
+ IPRT_LINUX_SAVE_EFL_AC();
+
+ /*
+ * Report the GSO capabilities of the host and device (if connected).
+ * Note! No need to mark ourselves busy here.
+ */
+ /** @todo duplicate work here now? Attach */
+#if defined(VBOXNETFLT_WITH_GSO_XMIT_HOST)
+ Log3(("vboxNetFltOsConnectIt: reporting host tso tso6\n"));
+ pThis->pSwitchPort->pfnReportGsoCapabilities(pThis->pSwitchPort,
+ 0
+ | RT_BIT_32(PDMNETWORKGSOTYPE_IPV4_TCP)
+ | RT_BIT_32(PDMNETWORKGSOTYPE_IPV6_TCP)
+ , INTNETTRUNKDIR_HOST);
+
+#endif
+ vboxNetFltLinuxReportNicGsoCapabilities(pThis);
+
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return VINF_SUCCESS;
+}
+
+
+void vboxNetFltOsDeleteInstance(PVBOXNETFLTINS pThis)
+{
+ struct net_device *pDev;
+ bool fRegistered;
+ IPRT_LINUX_SAVE_EFL_AC();
+
+#ifdef VBOXNETFLT_WITH_HOST2WIRE_FILTER
+ vboxNetFltLinuxUnhookDev(pThis, NULL);
+#endif
+
+ /** @todo This code may race vboxNetFltLinuxUnregisterDevice (very very
+ * unlikely, but none the less). Since it doesn't actually update the
+ * state (just reads it), it is likely to panic in some interesting
+ * ways. */
+
+ RTSpinlockAcquire(pThis->hSpinlock);
+ pDev = ASMAtomicUoReadPtrT(&pThis->u.s.pDev, struct net_device *);
+ fRegistered = ASMAtomicXchgBool(&pThis->u.s.fRegistered, false);
+ RTSpinlockRelease(pThis->hSpinlock);
+
+ if (fRegistered)
+ {
+ vboxNetFltSetLinkState(pThis, pDev, false);
+
+#ifndef VBOXNETFLT_LINUX_NO_XMIT_QUEUE
+ skb_queue_purge(&pThis->u.s.XmitQueue);
+#endif
+ Log(("vboxNetFltOsDeleteInstance: this=%p: xmit queue purged.\n", pThis));
+ Log(("vboxNetFltOsDeleteInstance: Device %p(%s) released. ref=%d\n",
+ pDev, pDev->name,
+#if RTLNX_VER_MIN(2,6,37)
+ netdev_refcnt_read(pDev)
+#else
+ atomic_read(&pDev->refcnt)
+#endif
+ ));
+ dev_put(pDev);
+ }
+
+ unregister_inet6addr_notifier(&pThis->u.s.NotifierIPv6);
+ unregister_inetaddr_notifier(&pThis->u.s.NotifierIPv4);
+
+ Log(("vboxNetFltOsDeleteInstance: this=%p: Notifier removed.\n", pThis));
+ unregister_netdevice_notifier(&pThis->u.s.Notifier);
+ module_put(THIS_MODULE);
+
+ IPRT_LINUX_RESTORE_EFL_AC();
+}
+
+
+int vboxNetFltOsInitInstance(PVBOXNETFLTINS pThis, void *pvContext)
+{
+ int err;
+ IPRT_LINUX_SAVE_EFL_AC();
+ NOREF(pvContext);
+
+ pThis->u.s.Notifier.notifier_call = vboxNetFltLinuxNotifierCallback;
+ err = register_netdevice_notifier(&pThis->u.s.Notifier);
+ if (err)
+ {
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return VERR_INTNET_FLT_IF_FAILED;
+ }
+ if (!pThis->u.s.fRegistered)
+ {
+ unregister_netdevice_notifier(&pThis->u.s.Notifier);
+ LogRel(("VBoxNetFlt: failed to find %s.\n", pThis->szName));
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return VERR_INTNET_FLT_IF_NOT_FOUND;
+ }
+
+ Log(("vboxNetFltOsInitInstance: this=%p: Notifier installed.\n", pThis));
+ if ( pThis->fDisconnectedFromHost
+ || !try_module_get(THIS_MODULE))
+ {
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return VERR_INTNET_FLT_IF_FAILED;
+ }
+
+ if (pThis->pSwitchPort->pfnNotifyHostAddress)
+ {
+ VBOXNETFLTNOTIFIER Enumerator;
+
+ /*
+ * register_inetaddr_notifier() and register_inet6addr_notifier()
+ * do not call the callback for existing devices. Enumerating
+ * all network devices explicitly is a bit of an ifdef mess,
+ * so co-opt register_netdevice_notifier() to do that for us.
+ */
+ RT_ZERO(Enumerator);
+ Enumerator.Notifier.notifier_call = vboxNetFltLinuxEnumeratorCallback;
+ Enumerator.pThis = pThis;
+
+ err = register_netdevice_notifier(&Enumerator.Notifier);
+ if (err)
+ {
+ LogRel(("%s: failed to enumerate network devices: error %d\n", __FUNCTION__, err));
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return VINF_SUCCESS;
+ }
+
+ unregister_netdevice_notifier(&Enumerator.Notifier);
+
+ pThis->u.s.NotifierIPv4.notifier_call = vboxNetFltLinuxNotifierIPv4Callback;
+ err = register_inetaddr_notifier(&pThis->u.s.NotifierIPv4);
+ if (err)
+ LogRel(("%s: failed to register IPv4 notifier: error %d\n", __FUNCTION__, err));
+
+ pThis->u.s.NotifierIPv6.notifier_call = vboxNetFltLinuxNotifierIPv6Callback;
+ err = register_inet6addr_notifier(&pThis->u.s.NotifierIPv6);
+ if (err)
+ LogRel(("%s: failed to register IPv6 notifier: error %d\n", __FUNCTION__, err));
+ }
+
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return VINF_SUCCESS;
+}
+
+int vboxNetFltOsPreInitInstance(PVBOXNETFLTINS pThis)
+{
+ IPRT_LINUX_SAVE_EFL_AC();
+
+ /*
+ * Init the linux specific members.
+ */
+ ASMAtomicUoWriteNullPtr(&pThis->u.s.pDev);
+ pThis->u.s.fRegistered = false;
+ pThis->u.s.fPromiscuousSet = false;
+ pThis->u.s.fPacketHandler = false;
+ memset(&pThis->u.s.PacketType, 0, sizeof(pThis->u.s.PacketType));
+#ifndef VBOXNETFLT_LINUX_NO_XMIT_QUEUE
+ skb_queue_head_init(&pThis->u.s.XmitQueue);
+# if RTLNX_VER_MIN(2,6,20)
+ INIT_WORK(&pThis->u.s.XmitTask, vboxNetFltLinuxXmitTask);
+# else
+ INIT_WORK(&pThis->u.s.XmitTask, vboxNetFltLinuxXmitTask, &pThis->u.s.XmitTask);
+# endif
+#endif
+
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return VINF_SUCCESS;
+}
+
+
+void vboxNetFltPortOsNotifyMacAddress(PVBOXNETFLTINS pThis, void *pvIfData, PCRTMAC pMac)
+{
+ NOREF(pThis); NOREF(pvIfData); NOREF(pMac);
+}
+
+
+int vboxNetFltPortOsConnectInterface(PVBOXNETFLTINS pThis, void *pvIf, void **pvIfData)
+{
+ /* Nothing to do */
+ NOREF(pThis); NOREF(pvIf); NOREF(pvIfData);
+ return VINF_SUCCESS;
+}
+
+
+int vboxNetFltPortOsDisconnectInterface(PVBOXNETFLTINS pThis, void *pvIfData)
+{
+ /* Nothing to do */
+ NOREF(pThis); NOREF(pvIfData);
+ return VINF_SUCCESS;
+}
+
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/linux/files_vboxnetflt b/src/VBox/HostDrivers/VBoxNetFlt/linux/files_vboxnetflt
new file mode 100755
index 00000000..f2f8f1f4
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/linux/files_vboxnetflt
@@ -0,0 +1,113 @@
+#!/bin/sh
+# $Id: files_vboxnetflt $
+## @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 <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+VBOX_VBOXNETFLT_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-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/intnetinline.h=>include/VBox/intnetinline.h \
+ ${PATH_ROOT}/include/VBox/vmm/pdmnetinline.h=>include/VBox/vmm/pdmnetinline.h \
+ ${PATH_ROOT}/include/VBox/param.h=>include/VBox/param.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/VBoxNetFlt/linux/VBoxNetFlt-linux.c=>linux/VBoxNetFlt-linux.c \
+ ${PATH_ROOT}/src/VBox/HostDrivers/VBoxNetFlt/VBoxNetFlt.c=>VBoxNetFlt.c \
+ ${PATH_ROOT}/src/VBox/HostDrivers/VBoxNetFlt/VBoxNetFltInternal.h=>VBoxNetFltInternal.h \
+ ${PATH_ROOT}/src/VBox/HostDrivers/Support/SUPDrvIDC.h=>SUPDrvIDC.h \
+ ${PATH_ROOT}/src/VBox/HostDrivers/Support/SUPR0IdcClient.c=>SUPR0IdcClient.c \
+ ${PATH_ROOT}/src/VBox/HostDrivers/Support/SUPR0IdcClientComponent.c=>SUPR0IdcClientComponent.c \
+ ${PATH_ROOT}/src/VBox/HostDrivers/Support/SUPR0IdcClientInternal.h=>SUPR0IdcClientInternal.h \
+ ${PATH_ROOT}/src/VBox/HostDrivers/Support/linux/SUPR0IdcClient-linux.c=>linux/SUPR0IdcClient-linux.c \
+ ${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/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/VBoxNetFlt/solaris/VBoxNetFlt-solaris.c b/src/VBox/HostDrivers/VBoxNetFlt/solaris/VBoxNetFlt-solaris.c
new file mode 100644
index 00000000..f455ec3c
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/solaris/VBoxNetFlt-solaris.c
@@ -0,0 +1,4042 @@
+/* $Id: VBoxNetFlt-solaris.c $ */
+/** @file
+ * VBoxNetFlt - Network Filter Driver (Host), Solaris Specific Code.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_NET_FLT_DRV
+#include <VBox/log.h>
+#include <VBox/err.h>
+#include <VBox/intnetinline.h>
+#include <VBox/version.h>
+#include <iprt/string.h>
+#include <iprt/initterm.h>
+#include <iprt/assert.h>
+#include <iprt/alloca.h>
+#include <iprt/net.h>
+#include <iprt/mem.h>
+#include <iprt/thread.h>
+#include <iprt/spinlock.h>
+#include <iprt/crc.h>
+#include <iprt/err.h>
+#include <iprt/ctype.h>
+#define VBOXNETFLT_SOLARIS_IPV6_POLLING
+#ifdef VBOXNETFLT_SOLARIS_IPV6_POLLING
+# include <iprt/timer.h>
+# include <iprt/time.h>
+#endif
+
+#include <inet/ip.h>
+#include <net/if.h>
+#include <sys/socket.h>
+#include <sys/kstr.h>
+#include <sys/file.h>
+#include <sys/sockio.h>
+#include <sys/strsubr.h>
+#include <sys/pathname.h>
+#include <sys/t_kuser.h>
+
+#include <sys/types.h>
+#include <sys/dlpi.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/param.h>
+#include <sys/ethernet.h>
+#include <sys/stat.h>
+#include <sys/stream.h>
+#include <sys/stropts.h>
+#include <sys/strsun.h>
+#include <sys/modctl.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/sunldi.h>
+#include <sys/ctf_api.h>
+
+// Workaround for very strange define in sys/user.h
+// #define u (curproc->p_user) /* user is now part of proc structure */
+#ifdef u
+#undef u
+#endif
+
+#define VBOXNETFLT_OS_SPECFIC 1
+#include "../VBoxNetFltInternal.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** The module name. */
+#define DEVICE_NAME "vboxflt"
+/** The module descriptions as seen in 'modinfo'. */
+#define DEVICE_DESC_DRV "VirtualBox NetDrv"
+#define DEVICE_DESC_MOD "VirtualBox NetMod"
+
+#ifdef VBOXNETFLT_SOLARIS_IPV6_POLLING
+/** Driver properties */
+# define VBOXNETFLT_IP6POLLINTERVAL "ipv6-pollinterval"
+#endif
+
+/** Maximum loopback packet queue size per interface */
+#define VBOXNETFLT_LOOPBACK_SIZE 32
+
+/** VLAN tag masking, should probably be in IPRT? */
+#define VLAN_ID(vlan) (((vlan) >> 0) & 0x0fffu)
+#define VLAN_CFI(vlan) (((vlan) >> 12) & 0x0001u)
+#define VLAN_PRI(vlan) (((vlan) >> 13) & 0x0007u)
+#define VLAN_TAG(pri,cfi,vid) (((pri) << 13) | ((cfi) << 12) | ((vid) << 0))
+
+typedef struct VLANHEADER
+{
+ uint16_t Type;
+ uint16_t Data;
+} VLANHEADER;
+typedef struct VLANHEADER *PVLANHEADER;
+
+
+/*********************************************************************************************************************************
+* Global Functions *
+*********************************************************************************************************************************/
+/**
+ * Stream Driver hooks.
+ */
+static int VBoxNetFltSolarisGetInfo(dev_info_t *pDip, ddi_info_cmd_t enmCmd, void *pArg, void **ppvResult);
+static int VBoxNetFltSolarisAttach(dev_info_t *pDip, ddi_attach_cmd_t enmCmd);
+static int VBoxNetFltSolarisDetach(dev_info_t *pDip, ddi_detach_cmd_t enmCmd);
+static int VBoxNetFltSolarisQuiesceNotNeeded(dev_info_t *pDip);
+
+/**
+ * Stream Module hooks.
+ */
+static int VBoxNetFltSolarisModOpen(queue_t *pQueue, dev_t *pDev, int fFile, int fStream, cred_t *pCred);
+static int VBoxNetFltSolarisModClose(queue_t *pQueue, int fFile, cred_t *pCred);
+static int VBoxNetFltSolarisModReadPut(queue_t *pQueue, mblk_t *pMsg);
+static int VBoxNetFltSolarisModWritePut(queue_t *pQueue, mblk_t *pMsg);
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Streams: module info.
+ */
+static struct module_info g_VBoxNetFltSolarisModInfo =
+{
+ 0xbad, /* 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_VBoxNetFltSolarisReadQ =
+{
+ VBoxNetFltSolarisModReadPut,
+ NULL, /* service */
+ VBoxNetFltSolarisModOpen,
+ VBoxNetFltSolarisModClose,
+ NULL, /* admin (reserved) */
+ &g_VBoxNetFltSolarisModInfo,
+ NULL /* module stats */
+};
+
+/**
+ * Streams: write queue hooks.
+ */
+static struct qinit g_VBoxNetFltSolarisWriteQ =
+{
+ VBoxNetFltSolarisModWritePut,
+ NULL, /* service */
+ NULL, /* open */
+ NULL, /* close */
+ NULL, /* admin (reserved) */
+ &g_VBoxNetFltSolarisModInfo,
+ NULL /* module stats */
+};
+
+/**
+ * Streams: IO stream tab.
+ */
+static struct streamtab g_VBoxNetFltSolarisStreamTab =
+{
+ &g_VBoxNetFltSolarisReadQ,
+ &g_VBoxNetFltSolarisWriteQ,
+ NULL, /* muxread init */
+ NULL /* muxwrite init */
+};
+
+/**
+ * cb_ops: driver char/block entry points
+ */
+static struct cb_ops g_VBoxNetFltSolarisCbOps =
+{
+ 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_VBoxNetFltSolarisStreamTab,
+ D_NEW | D_MP | D_MTQPAIR | D_MTOUTPERIM | D_MTOCEXCL, /* compat. flag */
+ CB_REV /* revision */
+};
+
+/**
+ * dev_ops: driver entry/exit and other ops.
+ */
+static struct dev_ops g_VBoxNetFltSolarisDevOps =
+{
+ DEVO_REV, /* driver build revision */
+ 0, /* ref count */
+ VBoxNetFltSolarisGetInfo,
+ nulldev, /* identify */
+ nulldev, /* probe */
+ VBoxNetFltSolarisAttach,
+ VBoxNetFltSolarisDetach,
+ nodev, /* reset */
+ &g_VBoxNetFltSolarisCbOps,
+ (struct bus_ops *)0,
+ nodev, /* power */
+ VBoxNetFltSolarisQuiesceNotNeeded
+};
+
+/**
+ * modldrv: export driver specifics to kernel
+ */
+static struct modldrv g_VBoxNetFltSolarisDriver =
+{
+ &mod_driverops, /* extern from kernel */
+ DEVICE_DESC_DRV " " VBOX_VERSION_STRING "r" RT_XSTR(VBOX_SVN_REV),
+ &g_VBoxNetFltSolarisDevOps
+};
+
+/**
+ * fmodsw: streams module ops
+ */
+static struct fmodsw g_VBoxNetFltSolarisModOps =
+{
+ DEVICE_NAME,
+ &g_VBoxNetFltSolarisStreamTab,
+ D_NEW | D_MP | D_MTQPAIR | D_MTOUTPERIM | D_MTOCEXCL
+};
+
+/**
+ * modlstrmod: streams module specifics to kernel
+ */
+static struct modlstrmod g_VBoxNetFltSolarisModule =
+{
+ &mod_strmodops, /* extern from kernel */
+ DEVICE_DESC_MOD " " VBOX_VERSION_STRING "r" RT_XSTR(VBOX_SVN_REV),
+ &g_VBoxNetFltSolarisModOps
+};
+
+/**
+ * modlinkage: export install/remove/info to the kernel
+ */
+static struct modlinkage g_VBoxNetFltSolarisModLinkage =
+{
+ MODREV_1, /* loadable module system revision */
+ {
+ &g_VBoxNetFltSolarisDriver, /* streams driver framework */
+ &g_VBoxNetFltSolarisModule, /* streams module framework */
+ NULL /* terminate array of linkage structures */
+ }
+};
+
+struct vboxnetflt_state_t;
+
+/**
+ * vboxnetflt_dladdr_t: DL SAP address format
+ */
+typedef struct vboxnetflt_dladdr_t
+{
+ ether_addr_t Mac;
+ uint16_t SAP;
+} vboxnetflt_dladdr_t;
+
+#define VBOXNETFLT_DLADDRL sizeof(vboxnetflt_dladdr_t)
+
+/**
+ * which stream is this?
+ */
+typedef enum VBOXNETFLTSTREAMTYPE
+{
+ kUndefined = 0,
+ kIp4Stream = 0x1b,
+ kIp6Stream = 0xcc,
+ kArpStream = 0xab,
+ kPromiscStream = 0xdf
+} VBOXNETFLTSTREAMTYPE;
+
+/**
+ * loopback packet identifier
+ */
+typedef struct VBOXNETFLTPACKETID
+{
+ struct VBOXNETFLTPACKETID *pNext;
+ uint16_t cbPacket;
+ uint16_t Checksum;
+ RTMAC SrcMac;
+ RTMAC DstMac;
+} VBOXNETFLTPACKETID;
+typedef struct VBOXNETFLTPACKETID *PVBOXNETFLTPACKETID;
+
+/**
+ * vboxnetflt_stream_t: per-stream data (multiple streams per interface)
+ */
+typedef struct vboxnetflt_stream_t
+{
+ int DevMinor; /* minor device no. (for clone) */
+ queue_t *pReadQueue; /* read side queue */
+ struct vboxnetflt_stream_t *pNext; /* next stream in list */
+ PVBOXNETFLTINS volatile pThis; /* the backend instance */
+ VBOXNETFLTSTREAMTYPE Type; /* the type of the stream */
+} vboxnetflt_stream_t;
+
+/**
+ * vboxnetflt_promisc_stream_t: per-interface dedicated stream data
+ */
+typedef struct vboxnetflt_promisc_stream_t
+{
+ vboxnetflt_stream_t Stream; /* dedicated/promiscuous stream */
+ bool fPromisc; /* cached promiscuous value */
+ bool fRawMode; /* whether raw mode request was successful */
+ uint32_t ModeReqId; /* track MIOCTLs for swallowing our fake request acknowledgements */
+#ifdef VBOXNETFLT_SOLARIS_IPV6_POLLING
+ PRTTIMER pIp6Timer; /* ipv6 stream poll timer for dynamic ipv6 stream attachment */
+#endif
+ size_t cLoopback; /* loopback queue size list */
+ timeout_id_t volatile TimeoutId; /* timeout id of promisc. req */
+ PVBOXNETFLTPACKETID pHead; /* loopback packet identifier head */
+ PVBOXNETFLTPACKETID pTail; /* loopback packet identifier tail */
+} vboxnetflt_promisc_stream_t;
+
+typedef struct vboxnetflt_promisc_params_t
+{
+ PVBOXNETFLTINS pThis; /* the backend instance */
+ bool fPromiscOn; /* whether promiscuous req. on or off */
+} vboxnetflt_promisc_params_t;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static int vboxNetFltSolarisSetRawMode(vboxnetflt_promisc_stream_t *pPromiscStream);
+/* static int vboxNetFltSolarisSetFastMode(queue_t *pQueue); */
+
+static int vboxNetFltSolarisPhysAddrReq(queue_t *pQueue);
+static void vboxNetFltSolarisCachePhysAddr(PVBOXNETFLTINS pThis, mblk_t *pPhysAddrAckMsg);
+static int vboxNetFltSolarisBindReq(queue_t *pQueue, int SAP);
+static int vboxNetFltSolarisNotifyReq(queue_t *pQueue);
+
+/* static int vboxNetFltSolarisUnitDataToRaw(PVBOXNETFLTINS pThis, mblk_t *pMsg, mblk_t **ppRawMsg); */
+static int vboxNetFltSolarisRawToUnitData(mblk_t *pMsg, mblk_t **ppDlpiMsg);
+
+static inline void vboxNetFltSolarisInitPacketId(PVBOXNETFLTPACKETID pTag, mblk_t *pMsg);
+static int vboxNetFltSolarisQueueLoopback(PVBOXNETFLTINS pThis, vboxnetflt_promisc_stream_t *pPromiscStream, mblk_t *pMsg);
+static bool vboxNetFltSolarisIsOurMBlk(PVBOXNETFLTINS pThis, vboxnetflt_promisc_stream_t *pPromiscStream, mblk_t *pMsg);
+
+static mblk_t *vboxNetFltSolarisMBlkFromSG(PVBOXNETFLTINS pThis, PINTNETSG pSG, uint32_t fDst);
+static unsigned vboxNetFltSolarisMBlkCalcSGSegs(PVBOXNETFLTINS pThis, mblk_t *pMsg);
+static int vboxNetFltSolarisMBlkToSG(PVBOXNETFLTINS pThis, mblk_t *pMsg, PINTNETSG pSG, unsigned cSegs, uint32_t fSrc);
+static int vboxNetFltSolarisRecv(PVBOXNETFLTINS pThis, vboxnetflt_stream_t *pStream, queue_t *pQueue, mblk_t *pMsg);
+/* static mblk_t *vboxNetFltSolarisFixChecksums(mblk_t *pMsg); */
+/* static void vboxNetFltSolarisAnalyzeMBlk(mblk_t *pMsg); */
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Global device info handle. */
+static dev_info_t *g_pVBoxNetFltSolarisDip = NULL;
+
+/** The (common) global data. */
+static VBOXNETFLTGLOBALS g_VBoxNetFltSolarisGlobals;
+
+/** The list of all opened streams. */
+vboxnetflt_stream_t *g_VBoxNetFltSolarisStreams = NULL;
+
+/** Global mutex protecting open/close. */
+static RTSEMFASTMUTEX g_VBoxNetFltSolarisMtx = NIL_RTSEMFASTMUTEX;
+
+/** Global credentials using during open/close. */
+static cred_t *g_pVBoxNetFltSolarisCred = NULL;
+
+/**
+ * g_VBoxNetFltInstance is the current PVBOXNETFLTINS to be associated with the stream being created
+ * in ModOpen. This is just shared global data between the dynamic attach and the ModOpen procedure.
+ */
+PVBOXNETFLTINS volatile g_VBoxNetFltSolarisInstance = NULL;
+
+/** Goes along with the instance to determine type of stream being opened/created. */
+VBOXNETFLTSTREAMTYPE volatile g_VBoxNetFltSolarisStreamType = kUndefined;
+
+#ifdef VBOXNETFLT_SOLARIS_IPV6_POLLING
+/** Global IPv6 polling interval */
+static int g_VBoxNetFltSolarisPollInterval = -1;
+#endif
+
+static int s_off_vnode = -1;
+#define VNODE_FOR_FILE_T(filetpointer) (*(struct vnode **)((char *)(filetpointer) + s_off_vnode))
+
+
+static int
+vboxNetFltSolarisCtfGetMemberOffset(ctf_file_t *pCtfFile, const char *pszStruct, const char *pszMember, int *pOffset)
+{
+ AssertReturn(pCtfFile, VERR_INVALID_PARAMETER);
+ AssertReturn(pszStruct, VERR_INVALID_PARAMETER);
+ AssertReturn(pszMember, VERR_INVALID_PARAMETER);
+ AssertReturn(pOffset, VERR_INVALID_PARAMETER);
+
+ ctf_id_t TypeId = ctf_lookup_by_name(pCtfFile, pszStruct);
+ if (TypeId != CTF_ERR)
+ {
+ ctf_membinfo_t MemberInfo;
+ bzero(&MemberInfo, sizeof(MemberInfo));
+ if (ctf_member_info(pCtfFile, TypeId, pszMember, &MemberInfo) != CTF_ERR)
+ {
+ *pOffset = (MemberInfo.ctm_offset >> 3);
+ LogRel((DEVICE_NAME ":%s::%s at %d\n", pszStruct, pszMember, *pOffset));
+ return VINF_SUCCESS;
+ }
+ else
+ LogRel((DEVICE_NAME ":ctf_member_info failed for struct %s member %s\n", pszStruct, pszMember));
+ }
+ else
+ LogRel((DEVICE_NAME ":ctf_lookup_by_name failed for struct %s\n", pszStruct));
+
+ return VERR_NOT_FOUND;
+}
+
+
+static int
+vboxNetFltSolarisProbeCtf(void)
+{
+ /*
+ * CTF probing for fluid f_vnode member in file_t.
+ */
+ int rc = VERR_INTERNAL_ERROR;
+ modctl_t *pModCtl = mod_hold_by_name("genunix");
+ if (pModCtl)
+ {
+ int err;
+ mutex_enter(&mod_lock);
+ ctf_file_t *pCtfFile = ctf_modopen(pModCtl->mod_mp, &err);
+ mutex_exit(&mod_lock);
+ if (pCtfFile)
+ {
+ rc = vboxNetFltSolarisCtfGetMemberOffset(pCtfFile, "file_t", "f_vnode", &s_off_vnode);
+ ctf_close(pCtfFile);
+ }
+ else
+ LogRel((DEVICE_NAME ":ctf_modopen failed. err=%d\n", err));
+
+ mod_release_mod(pModCtl);
+ }
+ else
+ LogRel((DEVICE_NAME ":mod_hold_by_name failed.\n"));
+
+ return rc;
+}
+
+
+/**
+ * Kernel entry points
+ */
+int _init(void)
+{
+ LogFunc((DEVICE_NAME ":_init\n"));
+
+ /*
+ * Prevent module autounloading.
+ */
+ modctl_t *pModCtl = mod_getctl(&g_VBoxNetFltSolarisModLinkage);
+ 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 = vboxNetFltSolarisProbeCtf();
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Initialize Solaris specific globals here.
+ */
+ g_VBoxNetFltSolarisStreams = NULL;
+ g_VBoxNetFltSolarisInstance = NULL;
+ g_pVBoxNetFltSolarisCred = crdup(kcred);
+ if (RT_LIKELY(g_pVBoxNetFltSolarisCred))
+ {
+ rc = RTSemFastMutexCreate(&g_VBoxNetFltSolarisMtx);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Initialize the globals and connect to the support driver.
+ *
+ * This will call back vboxNetFltOsOpenSupDrv (and maybe vboxNetFltOsCloseSupDrv)
+ * for establishing the connect to the support driver.
+ */
+ memset(&g_VBoxNetFltSolarisGlobals, 0, sizeof(g_VBoxNetFltSolarisGlobals));
+ rc = vboxNetFltInitGlobalsAndIdc(&g_VBoxNetFltSolarisGlobals);
+ if (RT_SUCCESS(rc))
+ {
+ rc = mod_install(&g_VBoxNetFltSolarisModLinkage);
+ if (!rc)
+ return rc;
+
+ LogRel((DEVICE_NAME ":mod_install failed. rc=%d\n", rc));
+ vboxNetFltTryDeleteIdcAndGlobals(&g_VBoxNetFltSolarisGlobals);
+ }
+ else
+ LogRel((DEVICE_NAME ":failed to initialize globals.\n"));
+
+ RTSemFastMutexDestroy(g_VBoxNetFltSolarisMtx);
+ g_VBoxNetFltSolarisMtx = NIL_RTSEMFASTMUTEX;
+ }
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":failed to allocate credentials.\n"));
+ rc = VERR_NO_MEMORY;
+ }
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisProbeCtf failed. rc=%d\n", rc));
+
+ RTR0Term();
+ }
+ else
+ LogRel((DEVICE_NAME ":failed to initialize IPRT (rc=%d)\n", rc));
+
+ memset(&g_VBoxNetFltSolarisGlobals, 0, sizeof(g_VBoxNetFltSolarisGlobals));
+ return RTErrConvertToErrno(rc);
+}
+
+
+int _fini(void)
+{
+ int rc;
+ LogFunc((DEVICE_NAME ":_fini\n"));
+
+ /*
+ * Undo the work done during start (in reverse order).
+ */
+ rc = vboxNetFltTryDeleteIdcAndGlobals(&g_VBoxNetFltSolarisGlobals);
+ if (RT_FAILURE(rc))
+ {
+ LogRel((DEVICE_NAME ":_fini - busy!\n"));
+ return EBUSY;
+ }
+
+ rc = mod_remove(&g_VBoxNetFltSolarisModLinkage);
+ if (!rc)
+ {
+ if (g_pVBoxNetFltSolarisCred)
+ {
+ crfree(g_pVBoxNetFltSolarisCred);
+ g_pVBoxNetFltSolarisCred = NULL;
+ }
+
+ if (g_VBoxNetFltSolarisMtx != NIL_RTSEMFASTMUTEX)
+ {
+ RTSemFastMutexDestroy(g_VBoxNetFltSolarisMtx);
+ g_VBoxNetFltSolarisMtx = NIL_RTSEMFASTMUTEX;
+ }
+
+ RTR0Term();
+ }
+
+ return rc;
+}
+
+
+int _info(struct modinfo *pModInfo)
+{
+ LogFunc((DEVICE_NAME ":_info\n"));
+
+ int rc = mod_info(&g_VBoxNetFltSolarisModLinkage, 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 VBoxNetFltSolarisAttach(dev_info_t *pDip, ddi_attach_cmd_t enmCmd)
+{
+ LogFunc((DEVICE_NAME ":VBoxNetFltSolarisAttach pDip=%p enmCmd=%d\n", pDip, enmCmd));
+
+ switch (enmCmd)
+ {
+ case DDI_ATTACH:
+ {
+ int rc = ddi_create_minor_node(pDip, DEVICE_NAME, S_IFCHR, 0 /* instance */, DDI_PSEUDO, CLONE_DEV);
+ if (rc == DDI_SUCCESS)
+ {
+ g_pVBoxNetFltSolarisDip = pDip;
+#ifdef VBOXNETFLT_SOLARIS_IPV6_POLLING
+ /*
+ * Get the user prop. for polling interval.
+ */
+ int Interval = ddi_getprop(DDI_DEV_T_ANY, pDip, DDI_PROP_DONTPASS, VBOXNETFLT_IP6POLLINTERVAL, -1 /* default */);
+ if (Interval == -1)
+ Log((DEVICE_NAME ":vboxNetFltSolarisSetupIp6Polling: no poll interval property specified. Skipping Ipv6 polling.\n"));
+ else if (Interval < 1 || Interval > 120)
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisSetupIp6Polling: Invalid polling interval %d. Expected between 1 and 120 secs.\n",
+ Interval));
+ Interval = -1;
+ }
+
+ g_VBoxNetFltSolarisPollInterval = Interval;
+#endif
+ ddi_report_dev(pDip);
+ return DDI_SUCCESS;
+ }
+ else
+ LogRel((DEVICE_NAME ":VBoxNetFltSolarisAttach failed to create minor node. rc%d\n", rc));
+ 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 VBoxNetFltSolarisDetach(dev_info_t *pDip, ddi_detach_cmd_t enmCmd)
+{
+ LogFunc((DEVICE_NAME ":VBoxNetFltSolarisDetach pDip=%p enmCmd=%d\n", pDip, enmCmd));
+
+ switch (enmCmd)
+ {
+ case DDI_DETACH:
+ {
+ ddi_remove_minor_node(pDip, NULL);
+ return DDI_SUCCESS;
+ }
+
+ case DDI_RESUME:
+ {
+ /* Nothing to do here... */
+ return DDI_SUCCESS;
+ }
+
+ /* case DDI_PM_SUSPEND: */
+ /* case DDI_HOT_PLUG_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 VBoxNetFltSolarisQuiesceNotNeeded(dev_info_t *pDip)
+{
+ return DDI_SUCCESS;
+}
+
+
+/**
+ * Info entry point, called by solaris kernel for obtaining driver info.
+ *
+ * @param pDip The module structure instance (do not use).
+ * @param enmCmd Information request type.
+ * @param pvArg Type specific argument.
+ * @param ppvResult Where to store the requested info.
+ *
+ * @returns corresponding solaris error code.
+ */
+static int VBoxNetFltSolarisGetInfo(dev_info_t *pDip, ddi_info_cmd_t enmCmd, void *pvArg, void **ppvResult)
+{
+ LogFunc((DEVICE_NAME ":VBoxNetFltSolarisGetInfo pDip=%p enmCmd=%d pArg=%p instance=%d\n", pDip, enmCmd,
+ getminor((dev_t)pvArg)));
+
+ switch (enmCmd)
+ {
+ case DDI_INFO_DEVT2DEVINFO:
+ {
+ *ppvResult = g_pVBoxNetFltSolarisDip;
+ return *ppvResult ? DDI_SUCCESS : DDI_FAILURE;
+ }
+
+ case DDI_INFO_DEVT2INSTANCE:
+ {
+ /* There can only be a single-instance of this driver and thus its instance number is 0. */
+ *ppvResult = (void *)0;
+ return DDI_SUCCESS;
+ }
+ }
+
+ return DDI_FAILURE;
+}
+
+
+/**
+ * Stream module open entry point, initializes the queue and allows streams processing.
+ *
+ * @param pQueue Pointer to the read queue (cannot be NULL).
+ * @param pDev Pointer to the dev_t associated with the driver at the end of the stream.
+ * @param fOpenMode Open mode (always 0 for streams driver, thus ignored).
+ * @param fStreamMode Stream open mode.
+ * @param pCred Pointer to user credentials.
+ *
+ * @returns corresponding solaris error code.
+ */
+static int VBoxNetFltSolarisModOpen(queue_t *pQueue, dev_t *pDev, int fOpenMode, int fStreamMode, cred_t *pCred)
+{
+ Assert(pQueue);
+
+ LogFunc((DEVICE_NAME ":VBoxNetFltSolarisModOpen pQueue=%p pDev=%p fOpenMode=%d fStreamMode=%d\n", pQueue, pDev,
+ fOpenMode, fStreamMode));
+
+ /*
+ * Already open?
+ */
+ if (pQueue->q_ptr)
+ {
+ LogRel((DEVICE_NAME ":VBoxNetFltSolarisModOpen invalid open.\n"));
+ return ENOENT;
+ }
+
+ /*
+ * Check that the request was initiated by our code.
+ *
+ * This ASSUMES that crdup() will return a copy with a unique address and
+ * not do any kind of clever pooling. This check will when combined with
+ * g_VBoxNetFltSolarisMtx prevent races and that the instance gets
+ * associated with the wrong streams.
+ */
+ if (pCred != g_pVBoxNetFltSolarisCred)
+ {
+ LogRel((DEVICE_NAME ":VBoxNetFltSolarisModOpen invalid credentials.\n"));
+ return EACCES;
+ }
+
+ /*
+ * Check for the VirtualBox instance.
+ */
+ PVBOXNETFLTINS pThis = g_VBoxNetFltSolarisInstance;
+ if (!pThis)
+ {
+ LogRel((DEVICE_NAME ":VBoxNetFltSolarisModOpen failed to get VirtualBox instance.\n"));
+ return ENOENT;
+ }
+
+ /*
+ * Check VirtualBox stream type.
+ */
+ if ( g_VBoxNetFltSolarisStreamType != kPromiscStream
+ && g_VBoxNetFltSolarisStreamType != kArpStream
+ && g_VBoxNetFltSolarisStreamType != kIp6Stream
+ && g_VBoxNetFltSolarisStreamType != kIp4Stream)
+ {
+ LogRel((DEVICE_NAME ":VBoxNetFltSolarisModOpen failed due to undefined VirtualBox open mode. Type=%d\n",
+ g_VBoxNetFltSolarisStreamType));
+ return ENOENT;
+ }
+
+ /*
+ * Get minor number. For clone opens provide a new dev_t.
+ */
+ minor_t DevMinor = 0;
+ vboxnetflt_stream_t *pStream = NULL;
+ vboxnetflt_stream_t **ppPrevStream = &g_VBoxNetFltSolarisStreams;
+ if (fStreamMode == CLONEOPEN)
+ {
+ for (; (pStream = *ppPrevStream) != NULL; ppPrevStream = &pStream->pNext)
+ {
+ if (DevMinor < pStream->DevMinor)
+ break;
+ DevMinor++;
+ }
+ *pDev = makedevice(getmajor(*pDev), DevMinor);
+ }
+ else
+ DevMinor = getminor(*pDev);
+
+ if (g_VBoxNetFltSolarisStreamType == kPromiscStream)
+ {
+ vboxnetflt_promisc_stream_t *pPromiscStream = RTMemAlloc(sizeof(vboxnetflt_promisc_stream_t));
+ if (RT_UNLIKELY(!pPromiscStream))
+ {
+ LogRel((DEVICE_NAME ":VBoxNetFltSolarisModOpen failed to allocate promiscuous stream data.\n"));
+ return ENOMEM;
+ }
+
+ pPromiscStream->fPromisc = false;
+ pPromiscStream->fRawMode = false;
+ pPromiscStream->ModeReqId = 0;
+ pPromiscStream->pHead = NULL;
+ pPromiscStream->pTail = NULL;
+ pPromiscStream->cLoopback = 0;
+ pPromiscStream->TimeoutId = 0;
+#ifdef VBOXNETFLT_SOLARIS_IPV6_POLLING
+ pPromiscStream->pIp6Timer = NULL;
+#endif
+ pStream = (vboxnetflt_stream_t *)pPromiscStream;
+ }
+ else
+ {
+ /*
+ * Allocate & initialize per-stream data. Hook it into the (read and write) queue's module specific data.
+ */
+ pStream = RTMemAlloc(sizeof(vboxnetflt_stream_t));
+ if (RT_UNLIKELY(!pStream))
+ {
+ LogRel((DEVICE_NAME ":VBoxNetFltSolarisModOpen failed to allocate stream data.\n"));
+ return ENOMEM;
+ }
+ }
+ pStream->DevMinor = DevMinor;
+ pStream->pReadQueue = pQueue;
+
+ /*
+ * Pick up the current global VBOXNETFLTINS instance as
+ * the one that we will associate this stream with.
+ */
+ ASMAtomicUoWritePtr(&pStream->pThis, pThis);
+ pStream->Type = g_VBoxNetFltSolarisStreamType;
+ switch (pStream->Type)
+ {
+ case kIp4Stream: ASMAtomicUoWritePtr((void**)&pThis->u.s.pIp4Stream, pStream); break;
+ case kIp6Stream: ASMAtomicUoWritePtr((void**)&pThis->u.s.pIp6Stream, pStream); break;
+ case kArpStream: ASMAtomicUoWritePtr((void**)&pThis->u.s.pArpStream, pStream); break;
+ case kPromiscStream: ASMAtomicUoWritePtr((void**)&pThis->u.s.pPromiscStream, pStream); break;
+ default: /* Heh. */
+ {
+ LogRel((DEVICE_NAME ":VBoxNetFltSolarisModOpen huh!? Invalid stream type %d\n", pStream->Type));
+ RTMemFree(pStream);
+ return EINVAL;
+ }
+ }
+
+ pQueue->q_ptr = pStream;
+ WR(pQueue)->q_ptr = pStream;
+
+ /*
+ * Link it to the list of streams.
+ */
+ pStream->pNext = *ppPrevStream;
+ *ppPrevStream = pStream;
+
+ /*
+ * Increment IntNet reference count for this stream.
+ */
+ vboxNetFltRetain(pThis, false /* fBusy */);
+
+ qprocson(pQueue);
+
+ /*
+ * Don't hold the spinlocks across putnext calls as it could
+ * (and does mostly) re-enter the put procedure on the same thread.
+ */
+ if (pStream->Type == kPromiscStream)
+ {
+ vboxnetflt_promisc_stream_t *pPromiscStream = (vboxnetflt_promisc_stream_t *)pStream;
+
+ /*
+ * Bind to SAP 0 (DL_ETHER).
+ * Note: We don't support DL_TPR (token passing ring) SAP as that is unnecessary asynchronous
+ * work to get DL_INFO_REQ acknowledgements and determine SAP based on the Mac Type etc.
+ * Besides TPR doesn't really exist anymore practically as far as I know.
+ */
+ int rc = vboxNetFltSolarisBindReq(pStream->pReadQueue, 0 /* SAP */);
+ if (RT_LIKELY(RT_SUCCESS(rc)))
+ {
+ /*
+ * Request the physical address (we cache the acknowledgement).
+ */
+ rc = vboxNetFltSolarisPhysAddrReq(pStream->pReadQueue);
+ if (RT_LIKELY(RT_SUCCESS(rc)))
+ {
+ /*
+ * Ask for DLPI link notifications, don't bother check for errors here.
+ */
+ vboxNetFltSolarisNotifyReq(pStream->pReadQueue);
+
+ /*
+ * Enable raw mode.
+ */
+ rc = vboxNetFltSolarisSetRawMode(pPromiscStream);
+ if (RT_FAILURE(rc))
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisSetRawMode failed rc=%Rrc.\n", rc));
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisSetRawMode failed rc=%Rrc.\n", rc));
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisBindReq failed rc=%Rrc.\n", rc));
+ }
+
+ NOREF(fOpenMode);
+
+ Log((DEVICE_NAME ":VBoxNetFltSolarisModOpen returns 0, DevMinor=%d pQueue=%p\n", DevMinor, pStream->pReadQueue));
+
+ return 0;
+}
+
+
+/**
+ * Stream module close entry point, undoes the work done on open and closes the stream.
+ *
+ * @param pQueue Pointer to the read queue (cannot be NULL).
+ * @param fOpenMode Open mode (always 0 for streams driver, thus ignored).
+ * @param pCred Pointer to user credentials.
+ *
+ * @returns corresponding solaris error code.
+ */
+static int VBoxNetFltSolarisModClose(queue_t *pQueue, int fOpenMode, cred_t *pCred)
+{
+ Assert(pQueue);
+
+ LogFunc((DEVICE_NAME ":VBoxNetFltSolarisModClose pQueue=%p fOpenMode=%d\n", pQueue, fOpenMode));
+
+ vboxnetflt_stream_t *pStream = NULL;
+ vboxnetflt_stream_t **ppPrevStream = NULL;
+
+ /*
+ * Get instance data.
+ */
+ pStream = (vboxnetflt_stream_t *)pQueue->q_ptr;
+ if (RT_UNLIKELY(!pStream))
+ {
+ LogRel((DEVICE_NAME ":VBoxNetFltSolarisModClose failed to get stream.\n"));
+ return ENXIO;
+ }
+
+ if (pStream->Type == kPromiscStream)
+ {
+ /*
+ * If there are any timeout scheduled, we need to make sure they are cancelled.
+ */
+ vboxnetflt_promisc_stream_t *pPromiscStream = (vboxnetflt_promisc_stream_t *)pStream;
+ timeout_id_t TimeoutId = ASMAtomicReadPtr(&pPromiscStream->TimeoutId);
+ if (TimeoutId)
+ {
+ quntimeout(WR(pPromiscStream->Stream.pReadQueue), TimeoutId);
+ ASMAtomicWritePtr(&pPromiscStream->TimeoutId, NULL);
+ }
+
+ flushq(pQueue, FLUSHALL);
+ flushq(WR(pQueue), FLUSHALL);
+ }
+
+ qprocsoff(pQueue);
+
+ if (pStream->Type == kPromiscStream)
+ {
+ vboxnetflt_promisc_stream_t *pPromiscStream = (vboxnetflt_promisc_stream_t *)pStream;
+
+ mutex_enter(&pStream->pThis->u.s.hMtx);
+
+ /*
+ * Free-up loopback buffers.
+ */
+ PVBOXNETFLTPACKETID pCur = pPromiscStream->pHead;
+ while (pCur)
+ {
+ PVBOXNETFLTPACKETID pNext = pCur->pNext;
+ RTMemFree(pCur);
+ pCur = pNext;
+ }
+ pPromiscStream->pHead = NULL;
+ pPromiscStream->pTail = NULL;
+ pPromiscStream->cLoopback = 0;
+
+#ifdef VBOXNETFLT_SOLARIS_IPV6_POLLING
+ /*
+ * Sheer paranoia.
+ */
+ if (pPromiscStream->pIp6Timer != NULL)
+ {
+ RTTimerStop(pPromiscStream->pIp6Timer);
+ RTTimerDestroy(pPromiscStream->pIp6Timer);
+ ASMAtomicUoWriteNullPtr(&pPromiscStream->pIp6Timer);
+ }
+#endif
+
+ mutex_exit(&pStream->pThis->u.s.hMtx);
+ }
+
+ /*
+ * Unlink it from the list of streams.
+ */
+ for (ppPrevStream = &g_VBoxNetFltSolarisStreams; (pStream = *ppPrevStream) != NULL; ppPrevStream = &pStream->pNext)
+ if (pStream == (vboxnetflt_stream_t *)pQueue->q_ptr)
+ break;
+ *ppPrevStream = pStream->pNext;
+
+ /*
+ * Delete the stream.
+ */
+ switch (pStream->Type)
+ {
+ case kIp4Stream: ASMAtomicUoWriteNullPtr(&pStream->pThis->u.s.pIp4Stream); break;
+ case kIp6Stream: ASMAtomicUoWriteNullPtr(&pStream->pThis->u.s.pIp6Stream); break;
+ case kArpStream: ASMAtomicUoWriteNullPtr(&pStream->pThis->u.s.pArpStream); break;
+ case kPromiscStream: ASMAtomicUoWriteNullPtr(&pStream->pThis->u.s.pPromiscStream); break;
+ default: /* Heh. */
+ {
+ AssertRelease(pStream->Type);
+ break;
+ }
+ }
+
+ /*
+ * Decrement IntNet reference count for this stream.
+ */
+ vboxNetFltRelease(pStream->pThis, false /* fBusy */);
+
+ RTMemFree(pStream);
+ pQueue->q_ptr = NULL;
+ WR(pQueue)->q_ptr = NULL;
+
+ NOREF(fOpenMode);
+ NOREF(pCred);
+
+ return 0;
+}
+
+
+/**
+ * Read side put procedure for processing messages in the read queue.
+ * All streams, bound and unbound share this read procedure.
+ *
+ * @param pQueue Pointer to the read queue.
+ * @param pMsg Pointer to the message.
+ *
+ * @returns corresponding solaris error code.
+ */
+static int VBoxNetFltSolarisModReadPut(queue_t *pQueue, mblk_t *pMsg)
+{
+ if (!pMsg)
+ return 0;
+
+ LogFunc((DEVICE_NAME ":VBoxNetFltSolarisModReadPut pQueue=%p pMsg=%p\n", pQueue, pMsg));
+
+ bool fSendUpstream = true;
+ vboxnetflt_stream_t *pStream = pQueue->q_ptr;
+ PVBOXNETFLTINS pThis = NULL;
+
+ /*
+ * In the unlikely case where VirtualBox crashed and this filter
+ * is somehow still in the host stream we must try not to panic the host.
+ */
+ if ( pStream
+ && pStream->Type == kPromiscStream)
+ {
+ fSendUpstream = false;
+ pThis = ASMAtomicUoReadPtrT(&pStream->pThis, PVBOXNETFLTINS);
+ if (RT_LIKELY(pThis))
+ {
+ /*
+ * Retain the instance if we're filtering regardless of we are active or not
+ * The reason being even when we are inactive we reference the instance (e.g
+ * the promiscuous OFF acknowledgement case).
+ */
+ RTSpinlockAcquire(pThis->hSpinlock);
+ const bool fActive = pThis->enmTrunkState == INTNETTRUNKIFSTATE_ACTIVE;
+ vboxNetFltRetain(pThis, true /* fBusy */);
+ RTSpinlockRelease(pThis->hSpinlock);
+
+ vboxnetflt_promisc_stream_t *pPromiscStream = (vboxnetflt_promisc_stream_t *)pStream;
+
+ switch (DB_TYPE(pMsg))
+ {
+ case M_DATA:
+ {
+ Log((DEVICE_NAME ":VBoxNetFltSolarisModReadPut M_DATA\n"));
+
+ if ( fActive
+ && pPromiscStream->fRawMode)
+ {
+ vboxNetFltSolarisRecv(pThis, pStream, pQueue, pMsg);
+ }
+ break;
+ }
+
+ case M_PROTO:
+ case M_PCPROTO:
+ {
+ union DL_primitives *pPrim = (union DL_primitives *)pMsg->b_rptr;
+ t_uscalar_t Prim = pPrim->dl_primitive;
+
+ Log((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: M_PCPROTO %d\n", Prim));
+ switch (Prim)
+ {
+ case DL_NOTIFY_IND:
+ {
+ if (MBLKL(pMsg) < DL_NOTIFY_IND_SIZE)
+ {
+ LogRel((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: Invalid notification size; expected>=%d"
+ " got=%d\n", DL_NOTIFY_IND_SIZE, MBLKL(pMsg)));
+ break;
+ }
+
+ dl_notify_ind_t *pNotifyInd = (dl_notify_ind_t *)pMsg->b_rptr;
+ switch (pNotifyInd->dl_notification)
+ {
+ case DL_NOTE_PHYS_ADDR:
+ {
+ if (pNotifyInd->dl_data != DL_CURR_PHYS_ADDR)
+ break;
+
+ size_t cOffset = pNotifyInd->dl_addr_offset;
+ size_t cbAddr = pNotifyInd->dl_addr_length;
+
+ if (!cOffset || !cbAddr)
+ {
+ LogRel((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: DL_NOTE_PHYS_ADDR."
+ "Invalid offset/addr.\n"));
+ fSendUpstream = false;
+ break;
+ }
+
+ bcopy(pMsg->b_rptr + cOffset, &pThis->u.s.MacAddr, sizeof(pThis->u.s.MacAddr));
+ Log((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: DL_NOTE_PHYS_ADDR. New Mac=%.*Rhxs\n",
+ sizeof(pThis->u.s.MacAddr), &pThis->u.s.MacAddr));
+ break;
+ }
+
+ case DL_NOTE_LINK_UP:
+ {
+ if (ASMAtomicXchgBool(&pThis->fDisconnectedFromHost, false))
+ Log((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: DL_NOTE_LINK_UP.\n"));
+ break;
+ }
+
+ case DL_NOTE_LINK_DOWN:
+ {
+ if (!ASMAtomicXchgBool(&pThis->fDisconnectedFromHost, true))
+ Log((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: DL_NOTE_LINK_DOWN.\n"));
+ break;
+ }
+ }
+ break;
+ }
+
+ case DL_BIND_ACK:
+ {
+ /*
+ * Swallow our bind request acknowledgement.
+ */
+ Log((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: DL_BIND_ACK. Bound to requested SAP!\n"));
+ break;
+ }
+
+ case DL_PHYS_ADDR_ACK:
+ {
+ /*
+ * Swallow our physical address request acknowledgement.
+ */
+ vboxNetFltSolarisCachePhysAddr(pThis, pMsg);
+ break;
+ }
+
+ case DL_OK_ACK:
+ {
+ /*
+ * Swallow our fake promiscuous request acknowledgement.
+ */
+ dl_ok_ack_t *pOkAck = (dl_ok_ack_t *)pMsg->b_rptr;
+ if (pOkAck->dl_correct_primitive == DL_PROMISCON_REQ)
+ {
+ Log((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: M_PCPROTO: DL_OK_ACK: fPromisc is ON.\n"));
+ pPromiscStream->fPromisc = true;
+ }
+ else if (pOkAck->dl_correct_primitive == DL_PROMISCOFF_REQ)
+ {
+ Log((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: M_PCPROTO: DL_OK_ACK: fPromisc is OFF.\n"));
+ pPromiscStream->fPromisc = false;
+ }
+ break;
+ }
+ }
+ break;
+ }
+
+ case M_IOCACK:
+ {
+ /*
+ * Swallow our fake raw/fast path mode request acknowledgement.
+ */
+ struct iocblk *pIOC = (struct iocblk *)pMsg->b_rptr;
+ if (pIOC->ioc_id == pPromiscStream->ModeReqId)
+ {
+ pPromiscStream->fRawMode = true;
+ Log((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: Mode acknowledgement. RawMode is %s\n",
+ pPromiscStream->fRawMode ? "ON" : "OFF"));
+ }
+ break;
+ }
+
+ case M_IOCNAK:
+ {
+ /*
+ * Swallow our fake raw/fast path mode request not acknowledged.
+ */
+ struct iocblk *pIOC = (struct iocblk *)pMsg->b_rptr;
+ if (pIOC->ioc_id == pPromiscStream->ModeReqId)
+ {
+ pPromiscStream->fRawMode = false;
+ LogRel((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: WARNING! Mode not acknowledged. RawMode is %s\n",
+ pPromiscStream->fRawMode ? "ON" : "OFF"));
+ }
+ break;
+ }
+
+ case M_FLUSH:
+ {
+ /*
+ * We must support flushing queues.
+ */
+ Log((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: M_FLUSH\n"));
+ if (*pMsg->b_rptr & FLUSHR)
+ flushq(pQueue, FLUSHALL);
+ break;
+ }
+ }
+
+ vboxNetFltRelease(pThis, true /* fBusy */);
+ }
+ else
+ LogRel((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: Could not find VirtualBox instance!!\n"));
+ }
+
+ if (fSendUpstream)
+ {
+ /*
+ * Don't queue up things here, can cause bad things to happen when the system
+ * is under heavy loads and we need to jam across high priority messages which
+ * if it's not done properly will end up in an infinite loop.
+ */
+ putnext(pQueue, pMsg);
+ }
+ else
+ {
+ /*
+ * We need to free up the message if we don't pass it through.
+ */
+ freemsg(pMsg);
+ }
+
+ return 0;
+}
+
+
+/**
+ * Write side put procedure for processing messages in the write queue.
+ * All streams, bound and unbound share this write procedure.
+ *
+ * @param pQueue Pointer to the write queue.
+ * @param pMsg Pointer to the message.
+ *
+ * @returns corresponding solaris error code.
+ */
+static int VBoxNetFltSolarisModWritePut(queue_t *pQueue, mblk_t *pMsg)
+{
+ LogFunc((DEVICE_NAME ":VBoxNetFltSolarisModWritePut pQueue=%p pMsg=%p\n", pQueue, pMsg));
+
+ putnext(pQueue, pMsg);
+ return 0;
+}
+
+
+/**
+ * Put the stream in raw mode.
+ *
+ * @returns VBox status code.
+ * @param pPromiscStream Pointer to the read queue.
+ */
+static int vboxNetFltSolarisSetRawMode(vboxnetflt_promisc_stream_t *pPromiscStream)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarisSetRawMode pPromiscStream=%p\n", pPromiscStream));
+
+ mblk_t *pRawMsg = NULL;
+ pRawMsg = mkiocb(DLIOCRAW);
+ if (RT_UNLIKELY(!pRawMsg))
+ return VERR_NO_MEMORY;
+
+ queue_t *pQueue = pPromiscStream->Stream.pReadQueue;
+ if (!pQueue)
+ return VERR_INVALID_POINTER;
+
+ struct iocblk *pIOC = (struct iocblk *)pRawMsg->b_rptr;
+ pPromiscStream->ModeReqId = pIOC->ioc_id;
+ pIOC->ioc_count = 0;
+
+ qreply(pQueue, pRawMsg);
+ return VINF_SUCCESS;
+}
+
+
+#if 0
+/**
+ * Put the stream back in fast path mode.
+ *
+ * @returns VBox status code.
+ * @param pQueue Pointer to the read queue.
+ */
+static int vboxNetFltSolarisSetFastMode(queue_t *pQueue)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarisSetFastMode pQueue=%p\n", pQueue));
+
+ mblk_t *pFastMsg = mkiocb(DL_IOC_HDR_INFO);
+ if (RT_UNLIKELY(!pFastMsg))
+ return VERR_NO_MEMORY;
+
+ vboxnetflt_stream_t *pStream = pQueue->q_ptr;
+ struct iocblk *pIOC = (struct iocblk *)pFastMsg->b_rptr;
+ pStream->ModeReqId = pIOC->ioc_id;
+
+ size_t cbReq = sizeof(dl_unitdata_req_t) + sizeof(vboxnetflt_dladdr_t);
+ mblk_t *pDataReqMsg = allocb(cbReq, BPRI_MED);
+ if (RT_UNLIKELY(!pDataReqMsg))
+ return VERR_NO_MEMORY;
+
+ DB_TYPE(pDataReqMsg) = M_PROTO;
+ dl_unitdata_req_t *pDataReq = (dl_unitdata_req_t *)pDataReqMsg->b_rptr;
+ pDataReq->dl_primitive = DL_UNITDATA_REQ;
+ pDataReq->dl_dest_addr_length = sizeof(vboxnetflt_dladdr_t);
+ pDataReq->dl_dest_addr_offset = sizeof(dl_unitdata_req_t);
+ pDataReq->dl_priority.dl_min = 0;
+ pDataReq->dl_priority.dl_max = 0;
+
+ bzero(pDataReqMsg->b_rptr + sizeof(dl_unitdata_req_t), sizeof(vboxnetflt_dladdr_t));
+ pDataReqMsg->b_wptr = pDataReqMsg->b_rptr + cbReq;
+
+ /*
+ * Link the data format request message into the header ioctl message.
+ */
+ pFastMsg->b_cont = pDataReqMsg;
+ pIOC->ioc_count = msgdsize(pDataReqMsg);
+
+ qreply(pQueue, pFastMsg);
+ return VINF_SUCCESS;
+}
+#endif
+
+
+/**
+ * Callback function for qwriter to send promiscuous request messages
+ * downstream.
+ *
+ * @param pQueue Pointer to the write queue.
+ * @param fPromisc Whether to send promiscuous ON or OFF requests.
+ *
+ * @returns VBox status code.
+ */
+static int vboxNetFltSolarisPromiscReq(queue_t *pQueue, bool fPromisc)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarisPromiscReq pQueue=%p fPromisc=%d\n", pQueue, fPromisc));
+
+ t_uscalar_t Cmd;
+ size_t cbReq = 0;
+ if (fPromisc)
+ {
+ Cmd = DL_PROMISCON_REQ;
+ cbReq = DL_PROMISCON_REQ_SIZE;
+ }
+ else
+ {
+ Cmd = DL_PROMISCOFF_REQ;
+ cbReq = DL_PROMISCOFF_REQ_SIZE;
+ }
+
+ mblk_t *pPromiscPhysMsg = mexchange(NULL, NULL, cbReq, M_PROTO, Cmd);
+ if (RT_UNLIKELY(!pPromiscPhysMsg))
+ return VERR_NO_MEMORY;
+
+ mblk_t *pPromiscSapMsg = mexchange(NULL, NULL, cbReq, M_PROTO, Cmd);
+ if (RT_UNLIKELY(!pPromiscSapMsg))
+ {
+ freemsg(pPromiscPhysMsg);
+ return VERR_NO_MEMORY;
+ }
+
+ if (fPromisc)
+ {
+ ((dl_promiscon_req_t *)pPromiscPhysMsg->b_rptr)->dl_level = DL_PROMISC_PHYS;
+ ((dl_promiscon_req_t *)pPromiscSapMsg->b_rptr)->dl_level = DL_PROMISC_SAP;
+ }
+ else
+ {
+ ((dl_promiscoff_req_t *)pPromiscPhysMsg->b_rptr)->dl_level = DL_PROMISC_PHYS;
+ ((dl_promiscoff_req_t *)pPromiscSapMsg->b_rptr)->dl_level = DL_PROMISC_SAP;
+ }
+
+ putnext(pQueue, pPromiscPhysMsg);
+ putnext(pQueue, pPromiscSapMsg);
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Callback wrapper for qwriter() to safely send promiscuous requests. This is
+ * called at the outer perimeter with exclusive lock held.
+ *
+ * @param pQueue Pointer to the write queue.
+ * @param pMsg A one byte message indicates a Promisc ON, otherwise
+ * a promiscuous OFF request. See
+ * vboxNetFltSolarisPromiscReqWrap().
+ */
+static void vboxNetFltSolarisPromiscReqWrapExcl(queue_t *pQueue, mblk_t *pMsg)
+{
+ /*
+ * Paranoia.
+ */
+ AssertReturnVoid(pQueue);
+ if (RT_UNLIKELY(!pMsg))
+ LogRel((DEVICE_NAME ":VBoxNetFltSolarisPromiscReqWrapExcl pQueue=%p missing message!\n", pQueue));
+
+ bool fPromisc = (MBLKL(pMsg) == 1);
+ freemsg(pMsg);
+ pMsg = NULL;
+ int rc = vboxNetFltSolarisPromiscReq(pQueue, fPromisc);
+ if (RT_FAILURE(rc))
+ LogRel((DEVICE_NAME ":VBoxNetFltSolarisPromiscReqWrapExcl vboxNetFltSolarisPromiscReq failed. rc=%d\n", rc));
+}
+
+
+/**
+ * Callback wrapper for qtimeout() to safely send promiscuous requests. This is
+ * called at the inner perimeter with shared lock.
+ *
+ * @param pvData Pointer to vboxnetflt_promisc_params_t. See
+ * vboxNetFltPortOsSetActive().
+ */
+static void vboxNetFltSolarisPromiscReqWrap(void *pvData)
+{
+ vboxnetflt_promisc_params_t *pParams = pvData;
+ if (RT_LIKELY(pParams))
+ {
+ PVBOXNETFLTINS pThis = pParams->pThis;
+ vboxnetflt_promisc_stream_t *pPromiscStream = ASMAtomicUoReadPtrT(&pThis->u.s.pPromiscStream,
+ vboxnetflt_promisc_stream_t *);
+ if ( pPromiscStream
+ && pPromiscStream->Stream.pReadQueue)
+ {
+ /*
+ * Use size of message to indicate to qwriter callback whether it must send
+ * promiscuous On or Off messages. This is ugly but easier and more efficient than
+ * scheduling two separate qwriter callbacks with prepared messages to putnext.
+ */
+ size_t cbMsg = pParams->fPromiscOn ? 1 : 2;
+ mblk_t *pMsg = allocb(cbMsg, BPRI_HI);
+ if (RT_UNLIKELY(!pMsg))
+ {
+ LogRel((DEVICE_NAME ":Failed to alloc message of %u bytes\n", cbMsg));
+ return;
+ }
+
+ /*
+ * Move the data pointer so we can use MBLKL, as MBLKSIZE gets the db_lim which is
+ * always aligned.
+ */
+ pMsg->b_wptr += cbMsg;
+
+ /*
+ * Upgrade inner perimeter lock to exclusive outer perimeter lock and
+ * then call putnext while we are at the outer perimeter.
+ */
+ qwriter(WR(pPromiscStream->Stream.pReadQueue), pMsg, vboxNetFltSolarisPromiscReqWrapExcl, PERIM_OUTER);
+ ASMAtomicWritePtr(&pPromiscStream->TimeoutId, NULL);
+ }
+ RTMemFree(pParams);
+ }
+}
+
+
+/**
+ * Send a fake physical address request downstream.
+ *
+ * @returns VBox status code.
+ * @param pQueue Pointer to the read queue.
+ */
+static int vboxNetFltSolarisPhysAddrReq(queue_t *pQueue)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarisPhysAddrReq pQueue=%p\n", pQueue));
+
+ t_uscalar_t Cmd = DL_PHYS_ADDR_REQ;
+ size_t cbReq = DL_PHYS_ADDR_REQ_SIZE;
+ mblk_t *pPhysAddrMsg = mexchange(NULL, NULL, cbReq, M_PROTO, Cmd);
+ if (RT_UNLIKELY(!pPhysAddrMsg))
+ return VERR_NO_MEMORY;
+
+ dl_phys_addr_req_t *pPhysAddrReq = (dl_phys_addr_req_t *)pPhysAddrMsg->b_rptr;
+ pPhysAddrReq->dl_addr_type = DL_CURR_PHYS_ADDR;
+
+ qreply(pQueue, pPhysAddrMsg);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Cache the MAC address into the VirtualBox instance given a physical
+ * address acknowledgement message.
+ *
+ * @param pThis The instance.
+ * @param pMsg Pointer to the physical address acknowledgement message.
+ */
+static void vboxNetFltSolarisCachePhysAddr(PVBOXNETFLTINS pThis, mblk_t *pMsg)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarisCachePhysAddr pThis=%p pMsg=%p\n", pThis, pMsg));
+
+ AssertCompile(sizeof(RTMAC) == ETHERADDRL);
+ dl_phys_addr_ack_t *pPhysAddrAck = (dl_phys_addr_ack_t *)pMsg->b_rptr;
+ if (pPhysAddrAck->dl_addr_length == sizeof(pThis->u.s.MacAddr))
+ {
+ bcopy(pMsg->b_rptr + pPhysAddrAck->dl_addr_offset, &pThis->u.s.MacAddr, sizeof(pThis->u.s.MacAddr));
+
+ Log((DEVICE_NAME ":vboxNetFltSolarisCachePhysAddr: DL_PHYS_ADDR_ACK: Mac=%.*Rhxs\n",
+ sizeof(pThis->u.s.MacAddr), &pThis->u.s.MacAddr));
+
+ if (vboxNetFltTryRetainBusyNotDisconnected(pThis))
+ {
+ Assert(pThis->pSwitchPort);
+ if (pThis->pSwitchPort)
+ pThis->pSwitchPort->pfnReportMacAddress(pThis->pSwitchPort, &pThis->u.s.MacAddr);
+ vboxNetFltRelease(pThis, true /*fBusy*/);
+ }
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisCachePhysAddr: Invalid address size. expected=%d got=%d\n", ETHERADDRL,
+ pPhysAddrAck->dl_addr_length));
+ }
+}
+
+
+/**
+ * Prepare DLPI bind request to a SAP.
+ *
+ * @returns VBox status code.
+ * @param pQueue Pointer to the read queue.
+ * @param SAP The SAP to bind the stream to.
+ */
+static int vboxNetFltSolarisBindReq(queue_t *pQueue, int SAP)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarisBindReq SAP=%d\n", SAP));
+
+ mblk_t *pBindMsg = mexchange(NULL, NULL, DL_BIND_REQ_SIZE, M_PROTO, DL_BIND_REQ);
+ if (RT_UNLIKELY(!pBindMsg))
+ return VERR_NO_MEMORY;
+
+ dl_bind_req_t *pBindReq = (dl_bind_req_t *)pBindMsg->b_rptr;
+ pBindReq->dl_sap = SAP;
+ pBindReq->dl_max_conind = 0;
+ pBindReq->dl_conn_mgmt = 0;
+ pBindReq->dl_xidtest_flg = 0;
+ pBindReq->dl_service_mode = DL_CLDLS;
+
+ qreply(pQueue, pBindMsg);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Prepare DLPI notifications request.
+ *
+ * @returns VBox status code.
+ * @param pQueue Pointer to the read queue.
+ */
+static int vboxNetFltSolarisNotifyReq(queue_t *pQueue)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarisNotifyReq\n"));
+
+ mblk_t *pNotifyMsg = mexchange(NULL, NULL, DL_NOTIFY_REQ_SIZE, M_PROTO, DL_NOTIFY_REQ);
+ if (RT_UNLIKELY(!pNotifyMsg))
+ return VERR_NO_MEMORY;
+
+ dl_notify_req_t *pNotifyReq = (dl_notify_req_t *)pNotifyMsg->b_rptr;
+ pNotifyReq->dl_notifications = DL_NOTE_LINK_UP | DL_NOTE_LINK_DOWN | DL_NOTE_PHYS_ADDR;
+
+ qreply(pQueue, pNotifyMsg);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Opens the required device and returns the vnode_t associated with it.
+ * We require this for the funny attach/detach routine.
+ *
+ * @returns VBox status code.
+ * @param pszDev The device path.
+ * @param ppVNode Where to store the vnode_t pointer associated with the opened device.
+ * @param ppVNodeHeld Where to store the vnode_t required during closing of the device.
+ * @param ppUser Open handle required while closing the device.
+ */
+static int vboxNetFltSolarisOpenDev(char *pszDev, vnode_t **ppVNode, vnode_t **ppVNodeHeld, TIUSER **ppUser)
+{
+ int rc;
+ vnode_t *pVNodeHeld = NULL;
+ rc = lookupname(pszDev, UIO_SYSSPACE, FOLLOW, NULLVPP, &pVNodeHeld);
+ if ( !rc
+ && pVNodeHeld)
+ {
+ TIUSER *pUser;
+ rc = t_kopen((file_t *)NULL, pVNodeHeld->v_rdev, FREAD | FWRITE, &pUser, kcred);
+ if (!rc)
+ {
+ if ( pUser
+ && pUser->fp
+ && VNODE_FOR_FILE_T(pUser->fp))
+ {
+ *ppVNode = VNODE_FOR_FILE_T(pUser->fp);
+ *ppVNodeHeld = pVNodeHeld;
+ *ppUser = pUser;
+ return VINF_SUCCESS;
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisOpenDev failed. pUser=%p fp=%p f_vnode=%p\n", pUser,
+ pUser ? pUser->fp : NULL, pUser && pUser->fp ? VNODE_FOR_FILE_T(pUser->fp) : NULL));
+ }
+
+ if (pUser)
+ t_kclose(pUser, 0);
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisOpenDev t_kopen failed. rc=%d\n", rc));
+
+ VN_RELE(pVNodeHeld);
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisOpenDev lookupname failed. rc=%d pVNodeHeld=%p\n", rc, pVNodeHeld));
+
+ return VERR_PATH_NOT_FOUND;
+}
+
+
+/**
+ * Close the device opened using vboxNetFltSolarisOpenDev.
+ *
+ * @param pVNodeHeld Pointer to the held vnode of the device.
+ * @param pUser Pointer to the file handle.
+ */
+static void vboxNetFltSolarisCloseDev(vnode_t *pVNodeHeld, TIUSER *pUser)
+{
+ t_kclose(pUser, 0);
+ VN_RELE(pVNodeHeld);
+}
+
+
+/**
+ * Set the DLPI style-2 PPA via an attach request, Synchronous.
+ * Waits for request acknowledgement and verifies the result.
+ *
+ * @returns VBox status code.
+ * @param hDevice Layered device handle.
+ * @param PPA Physical Point of Attachment (PPA) number.
+ */
+static int vboxNetFltSolarisAttachReq(ldi_handle_t hDevice, int PPA)
+{
+ int rc;
+ mblk_t *pAttachMsg = mexchange(NULL, NULL, DL_ATTACH_REQ_SIZE, M_PROTO, DL_ATTACH_REQ);
+ if (RT_UNLIKELY(!pAttachMsg))
+ return VERR_NO_MEMORY;
+
+ dl_attach_req_t *pAttachReq = (dl_attach_req_t *)pAttachMsg->b_rptr;
+ pAttachReq->dl_ppa = PPA;
+
+ rc = ldi_putmsg(hDevice, pAttachMsg);
+ if (!rc)
+ {
+ rc = ldi_getmsg(hDevice, &pAttachMsg, NULL);
+ if (!rc)
+ {
+ /*
+ * Verify if the attach succeeded.
+ */
+ size_t cbMsg = MBLKL(pAttachMsg);
+ if (cbMsg >= sizeof(t_uscalar_t))
+ {
+ union DL_primitives *pPrim = (union DL_primitives *)pAttachMsg->b_rptr;
+ t_uscalar_t AckPrim = pPrim->dl_primitive;
+
+ if ( AckPrim == DL_OK_ACK /* Success! */
+ && cbMsg == DL_OK_ACK_SIZE)
+ {
+ rc = VINF_SUCCESS;
+ }
+ else if ( AckPrim == DL_ERROR_ACK /* Error Ack. */
+ && cbMsg == DL_ERROR_ACK_SIZE)
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachReq ldi_getmsg succeeded, but unsupported op.\n"));
+ rc = VERR_NOT_SUPPORTED;
+ }
+ else /* Garbled reply */
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachReq ldi_getmsg succeeded, but invalid op."
+ " expected %d recvd %d\n", DL_OK_ACK, AckPrim));
+ rc = VERR_INVALID_FUNCTION;
+ }
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachReq ldi_getmsg succeeded, but invalid size %d expected %d\n", cbMsg,
+ DL_OK_ACK_SIZE));
+ rc = VERR_INVALID_FUNCTION;
+ }
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachReq ldi_getmsg failed. rc=%d\n", rc));
+ rc = VERR_INVALID_FUNCTION;
+ }
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachReq ldi_putmsg failed. rc=%d\n", rc));
+ rc = VERR_UNRESOLVED_ERROR;
+ }
+
+ freemsg(pAttachMsg);
+ return rc;
+}
+
+
+/**
+ * Get the logical interface flags from the stream.
+ *
+ * @returns VBox status code.
+ * @param hDevice Layered device handle.
+ * @param pInterface Pointer to the interface.
+ */
+static int vboxNetFltSolarisGetIfFlags(ldi_handle_t hDevice, struct lifreq *pInterface)
+{
+ struct strioctl IOCReq;
+ int rc;
+ int ret;
+ IOCReq.ic_cmd = SIOCGLIFFLAGS;
+ IOCReq.ic_timout = 40;
+ IOCReq.ic_len = sizeof(struct lifreq);
+ IOCReq.ic_dp = (caddr_t)pInterface;
+ rc = ldi_ioctl(hDevice, I_STR, (intptr_t)&IOCReq, FKIOCTL, kcred, &ret);
+ if (!rc)
+ return VINF_SUCCESS;
+
+ return RTErrConvertFromErrno(rc);
+}
+
+
+/**
+ * Sets the multiplexor ID from the interface.
+ *
+ * @returns VBox status code.
+ * @param pVNode Pointer to the device vnode.
+ * @param pInterface Pointer to the interface.
+ */
+static int vboxNetFltSolarisSetMuxId(vnode_t *pVNode, struct lifreq *pInterface)
+{
+ struct strioctl IOCReq;
+ int rc;
+ int ret;
+ IOCReq.ic_cmd = SIOCSLIFMUXID;
+ IOCReq.ic_timout = 40;
+ IOCReq.ic_len = sizeof(struct lifreq);
+ IOCReq.ic_dp = (caddr_t)pInterface;
+
+ rc = strioctl(pVNode, I_STR, (intptr_t)&IOCReq, 0, K_TO_K, kcred, &ret);
+ if (!rc)
+ return VINF_SUCCESS;
+
+ return RTErrConvertFromErrno(rc);
+}
+
+
+/**
+ * Get the multiplexor file descriptor of the lower stream.
+ *
+ * @returns VBox status code.
+ * @param pVNode Pointer to the device vnode.
+ * @param MuxId The multiplexor ID.
+ * @param pFd Where to store the lower stream file descriptor.
+ */
+static int vboxNetFltSolarisMuxIdToFd(vnode_t *pVNode, int MuxId, int *pFd)
+{
+ int ret;
+
+ *pFd = -1; /* silence compiler warnings from -Wmaybe-uninitialized */
+ int rc = strioctl(pVNode, _I_MUXID2FD, (intptr_t)MuxId, 0, K_TO_K, kcred, &ret);
+ if (!rc)
+ {
+ *pFd = ret;
+ return VINF_SUCCESS;
+ }
+
+ return RTErrConvertFromErrno(rc);
+}
+
+
+/**
+ * Relinks the lower and the upper IPv4 stream.
+ *
+ * @returns VBox status code.
+ * @param pVNode Pointer to the device vnode.
+ * @param pInterface Pointer to the interface.
+ * @param IpMuxFd The IP multiplexor ID.
+ * @param ArpMuxFd The ARP multiplexor ID.
+ */
+static int vboxNetFltSolarisRelinkIp4(vnode_t *pVNode, struct lifreq *pInterface, int IpMuxFd, int ArpMuxFd)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarisRelinkIp4: pVNode=%p pInterface=%p IpMuxFd=%d ArpMuxFd=%d\n", pVNode,
+ pInterface, IpMuxFd, ArpMuxFd));
+
+ int NewIpMuxId;
+ int NewArpMuxId;
+ int rc = strioctl(pVNode, I_PLINK, (intptr_t)IpMuxFd, 0, K_TO_K, kcred, &NewIpMuxId);
+ int rc2 = strioctl(pVNode, I_PLINK, (intptr_t)ArpMuxFd, 0, K_TO_K, kcred, &NewArpMuxId);
+ if ( !rc
+ && !rc2)
+ {
+ pInterface->lifr_ip_muxid = NewIpMuxId;
+ pInterface->lifr_arp_muxid = NewArpMuxId;
+ rc = vboxNetFltSolarisSetMuxId(pVNode, pInterface);
+ if (RT_SUCCESS(rc))
+ return VINF_SUCCESS;
+
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisRelinkIp4: failed to set new Mux Id.\n"));
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisRelinkIp4: failed to link.\n"));
+
+ return VERR_GENERAL_FAILURE;
+}
+
+
+/**
+ * Relinks the lower and the upper IPv6 stream.
+ *
+ * @returns VBox status code.
+ * @param pVNode Pointer to the device vnode.
+ * @param pInterface Pointer to the interface.
+ * @param Ip6MuxFd The IPv6 multiplexor ID.
+ */
+static int vboxNetFltSolarisRelinkIp6(vnode_t *pVNode, struct lifreq *pInterface, int Ip6MuxFd)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarisRelinkIp6: pVNode=%p pInterface=%p Ip6MuxFd=%d\n", pVNode, pInterface, Ip6MuxFd));
+
+ int NewIp6MuxId;
+ int rc = strioctl(pVNode, I_PLINK, (intptr_t)Ip6MuxFd, 0, K_TO_K, kcred, &NewIp6MuxId);
+ if (!rc)
+ {
+ pInterface->lifr_ip_muxid = NewIp6MuxId;
+ rc = vboxNetFltSolarisSetMuxId(pVNode, pInterface);
+ if (RT_SUCCESS(rc))
+ return VINF_SUCCESS;
+
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisRelinkIp6: failed to set new Mux Id.\n"));
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisRelinkIp6: failed to link.\n"));
+
+ return VERR_GENERAL_FAILURE;
+}
+
+
+/**
+ * Dynamically find the position on the host stack where to attach/detach ourselves.
+ *
+ * @returns VBox status code.
+ * @param fAttach Is this an attach or detach.
+ * @param pVNode Pointer to the lower stream vnode.
+ * @param pModPos Where to store the module position.
+ */
+static int vboxNetFltSolarisDetermineModPos(bool fAttach, vnode_t *pVNode, int *pModPos)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarisDetermineModPos: fAttach=%d pVNode=%p pModPos=%p\n", fAttach, pVNode, pModPos));
+
+ int cMod;
+ int rc = strioctl(pVNode, I_LIST, (intptr_t)NULL, 0, K_TO_K, kcred, &cMod);
+ if (!rc)
+ {
+ if (cMod < 1)
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisDetermineModPos: too few modules on host interface. cMod=%d\n"));
+ return VERR_OUT_OF_RANGE;
+ }
+
+ /*
+ * While attaching we make sure we are at the bottom most of the stack, excepting
+ * the host driver.
+ */
+ Log((DEVICE_NAME ":vboxNetFltSolarisDetermineModPos: cMod=%d\n", cMod));
+ if (fAttach)
+ {
+ *pModPos = cMod - 1;
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Detaching is a bit more complicated; since user could have altered the stack positions
+ * we take the safe approach by finding our position.
+ */
+ struct str_list StrList;
+ StrList.sl_nmods = cMod;
+ StrList.sl_modlist = RTMemAllocZ(cMod * sizeof(struct str_list));
+ if (RT_UNLIKELY(!StrList.sl_modlist))
+ {
+ Log((DEVICE_NAME ":vboxNetFltSolarisDetermineModPos: failed to alloc memory for StrList.\n"));
+ return VERR_NO_MEMORY;
+ }
+
+ /*
+ * Get the list of all modules on the stack.
+ */
+ int ret;
+ rc = strioctl(pVNode, I_LIST, (intptr_t)&StrList, 0, K_TO_K, kcred, &ret);
+ if (!rc)
+ {
+ /*
+ * Find our filter.
+ */
+ for (int i = 0; i < StrList.sl_nmods; i++)
+ {
+ if (!strcmp(DEVICE_NAME, StrList.sl_modlist[i].l_name))
+ {
+ Log((DEVICE_NAME ":vboxNetFltSolarisDetermineModPos: Success! Found %s at %d.\n", DEVICE_NAME, i));
+ *pModPos = i;
+ RTMemFree(StrList.sl_modlist);
+ return VINF_SUCCESS;
+ }
+ }
+
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisDetermineModPos: failed to find %s in the host stack.\n", DEVICE_NAME));
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisDetermineModPos: failed to get module information. rc=%d\n"));
+
+ RTMemFree(StrList.sl_modlist);
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisDetermineModPos: failed to get list of modules on host interface. rc=%d\n", rc));
+ return VERR_GENERAL_FAILURE;
+}
+
+
+/**
+ * Opens up the DLPI style 2 link that requires explicit PPA attach
+ * phase.
+ *
+ * @returns VBox status code.
+ * @param pThis The instance.
+ * @param pDevId Where to store the opened LDI device id.
+ */
+static int vboxNetFltSolarisOpenStyle2(PVBOXNETFLTINS pThis, ldi_ident_t *pDevId)
+{
+ /*
+ * Strip out PPA from the device name, eg: "ce3".
+ */
+ char *pszDev = RTStrDup(pThis->szName);
+ if (!pszDev)
+ return VERR_NO_MEMORY;
+
+ char *pszEnd = strchr(pszDev, '\0');
+ while (--pszEnd > pszDev)
+ if (!RT_C_IS_DIGIT(*pszEnd))
+ break;
+ pszEnd++;
+
+ int rc = VERR_GENERAL_FAILURE;
+ long PPA = -1;
+ if ( pszEnd
+ && ddi_strtol(pszEnd, NULL, 10, &PPA) == 0)
+ {
+ *pszEnd = '\0';
+ char szDev[128];
+ RTStrPrintf(szDev, sizeof(szDev), "/dev/%s", pszDev);
+
+ /*
+ * Try open the device as DPLI style 2.
+ */
+ rc = ldi_open_by_name(szDev, FREAD | FWRITE, kcred, &pThis->u.s.hIface, *pDevId);
+ if (!rc)
+ {
+ /*
+ * Attach the PPA explictly.
+ */
+ rc = vboxNetFltSolarisAttachReq(pThis->u.s.hIface, (int)PPA);
+ if (RT_SUCCESS(rc))
+ {
+ RTStrFree(pszDev);
+ return rc;
+ }
+
+ ldi_close(pThis->u.s.hIface, FREAD | FWRITE, kcred);
+ pThis->u.s.hIface = NULL;
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisOpenStyle2 dl_attach failed. rc=%d szDev=%s PPA=%d rc=%d\n", rc, szDev, PPA));
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisOpenStyle2 Failed to open. rc=%d szDev=%s PPA=%d\n", rc, szDev, PPA));
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisOpenStyle2 Failed to construct PPA. pszDev=%s pszEnd=%s.\n", pszDev, pszEnd));
+
+ RTStrFree(pszDev);
+ return VERR_INTNET_FLT_IF_FAILED;
+}
+
+
+/**
+ * Opens up dedicated stream on top of the interface.
+ * As a side-effect, the stream gets opened during
+ * the I_PUSH phase.
+ *
+ * @param pThis The instance.
+ */
+static int vboxNetFltSolarisOpenStream(PVBOXNETFLTINS pThis)
+{
+ ldi_ident_t DevId;
+ DevId = ldi_ident_from_anon();
+ int ret;
+
+ /*
+ * Figure out if this is a VLAN interface or not based on the interface name.
+ * Only works for the VLAN PPA-hack based names. See @bugref{4854} for details.
+ */
+ char *pszEnd = strchr(pThis->szName, '\0');
+ while (--pszEnd > pThis->szName)
+ if (!RT_C_IS_DIGIT(*pszEnd))
+ break;
+ pszEnd++;
+ uint32_t PPA = RTStrToUInt32(pszEnd);
+ if (PPA > 1000)
+ {
+ pThis->u.s.fVLAN = true;
+ LogRel((DEVICE_NAME ": %s detected as VLAN interface with VID=%u.\n", pThis->szName, PPA / 1000U));
+ }
+
+ /*
+ * Try style-1 open first.
+ */
+ char szDev[128];
+ RTStrPrintf(szDev, sizeof(szDev), "/dev/net/%s", pThis->szName);
+ int rc = ldi_open_by_name(szDev, FREAD | FWRITE, kcred, &pThis->u.s.hIface, DevId);
+ if ( rc
+ && rc == ENODEV) /* ENODEV is returned when resolvepath fails, not ENOENT */
+ {
+ /*
+ * Fallback to non-ClearView style-1 open.
+ */
+ RTStrPrintf(szDev, sizeof(szDev), "/dev/%s", pThis->szName);
+ rc = ldi_open_by_name(szDev, FREAD | FWRITE, kcred, &pThis->u.s.hIface, DevId);
+ }
+
+ if (rc)
+ {
+ /*
+ * Try DLPI style 2.
+ */
+ rc = vboxNetFltSolarisOpenStyle2(pThis, &DevId);
+ if (RT_FAILURE(rc))
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisOpenStream vboxNetFltSolarisOpenStyle2 failed. rc=%d\n", rc));
+ else
+ rc = 0;
+ }
+
+ ldi_ident_release(DevId);
+ if (rc)
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisOpenStream Failed to open '%s' rc=%d pszName='%s'\n", szDev, rc, pThis->szName));
+ return VERR_INTNET_FLT_IF_FAILED;
+ }
+
+ rc = ldi_ioctl(pThis->u.s.hIface, I_FIND, (intptr_t)DEVICE_NAME, FKIOCTL, kcred, &ret);
+ if (!rc)
+ {
+ if (!ret)
+ {
+ if (RT_LIKELY(g_pVBoxNetFltSolarisCred)) /* Paranoia */
+ {
+ rc = RTSemFastMutexRequest(g_VBoxNetFltSolarisMtx);
+ AssertRCReturn(rc, rc);
+
+ g_VBoxNetFltSolarisInstance = pThis;
+ g_VBoxNetFltSolarisStreamType = kPromiscStream;
+
+ rc = ldi_ioctl(pThis->u.s.hIface, I_PUSH, (intptr_t)DEVICE_NAME, FKIOCTL, g_pVBoxNetFltSolarisCred, &ret);
+
+ g_VBoxNetFltSolarisInstance = NULL;
+ g_VBoxNetFltSolarisStreamType = kUndefined;
+
+ RTSemFastMutexRelease(g_VBoxNetFltSolarisMtx);
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisOpenStream huh!? Missing credentials.\n"));
+ rc = VERR_INVALID_POINTER;
+ }
+
+ if (!rc)
+ return VINF_SUCCESS;
+
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisOpenStream Failed to push filter onto host interface '%s'\n", pThis->szName));
+ }
+ else
+ return VINF_SUCCESS;
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisOpenStream Failed to search for filter in interface '%s'.\n", pThis->szName));
+
+ ldi_close(pThis->u.s.hIface, FREAD | FWRITE, kcred);
+ pThis->u.s.hIface = NULL;
+
+ return VERR_INTNET_FLT_IF_FAILED;
+}
+
+
+/**
+ * Closes the interface, thereby closing the dedicated stream.
+ *
+ * @param pThis The instance.
+ */
+static void vboxNetFltSolarisCloseStream(PVBOXNETFLTINS pThis)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarisCloseStream pThis=%p\n"));
+
+ if (pThis->u.s.hIface)
+ {
+ ldi_close(pThis->u.s.hIface, FREAD | FWRITE, kcred);
+ pThis->u.s.hIface = NULL;
+ }
+}
+
+
+/**
+ * Dynamically attach under IPv4 and ARP streams on the host stack.
+ *
+ * @returns VBox status code.
+ * @param pThis The instance.
+ * @param fAttach Is this an attach or detach.
+ */
+static int vboxNetFltSolarisAttachIp4(PVBOXNETFLTINS pThis, bool fAttach)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarisAttachIp4 pThis=%p fAttach=%d\n", pThis, fAttach));
+
+ /*
+ * Statutory Warning: Hackish code ahead.
+ */
+ char *pszModName = DEVICE_NAME;
+
+ struct lifreq Ip4Interface;
+ bzero(&Ip4Interface, sizeof(Ip4Interface));
+ Ip4Interface.lifr_addr.ss_family = AF_INET;
+ strncpy(Ip4Interface.lifr_name, pThis->szName, sizeof(Ip4Interface.lifr_name));
+
+ struct strmodconf StrMod;
+ StrMod.mod_name = pszModName;
+ StrMod.pos = -1; /* this is filled in later. */
+
+ struct strmodconf ArpStrMod;
+ bcopy(&StrMod, &ArpStrMod, sizeof(StrMod));
+
+ int rc;
+ int rc2;
+ int ret;
+ ldi_ident_t DeviceIdent = ldi_ident_from_anon();
+ ldi_handle_t Ip4DevHandle;
+ ldi_handle_t ArpDevHandle;
+
+ /*
+ * Open the IP and ARP streams as layered devices.
+ */
+ rc = ldi_open_by_name(IP_DEV_NAME, FREAD | FWRITE, kcred, &Ip4DevHandle, DeviceIdent);
+ if (rc)
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp4: failed to open the IP stream on '%s'.\n", pThis->szName));
+ ldi_ident_release(DeviceIdent);
+ return VERR_INTNET_FLT_IF_FAILED;
+ }
+
+ rc = ldi_open_by_name("/dev/arp", FREAD | FWRITE, kcred, &ArpDevHandle, DeviceIdent);
+ if (rc)
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp4: failed to open the ARP stream on '%s'.\n", pThis->szName));
+ ldi_ident_release(DeviceIdent);
+ ldi_close(Ip4DevHandle, FREAD | FWRITE, kcred);
+ return VERR_INTNET_FLT_IF_FAILED;
+ }
+
+ ldi_ident_release(DeviceIdent);
+
+ /*
+ * Obtain the interface flags from IPv4.
+ */
+ rc = vboxNetFltSolarisGetIfFlags(Ip4DevHandle, &Ip4Interface);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Open the UDP stream. We sort of cheat here and obtain the vnode so that we can perform
+ * things that are not possible from the layered interface.
+ */
+ vnode_t *pUdp4VNode = NULL;
+ vnode_t *pUdp4VNodeHeld = NULL;
+ TIUSER *pUdp4User = NULL;
+ rc = vboxNetFltSolarisOpenDev(UDP_DEV_NAME, &pUdp4VNode, &pUdp4VNodeHeld, &pUdp4User);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Get the multiplexor IDs.
+ */
+ rc = ldi_ioctl(Ip4DevHandle, SIOCGLIFMUXID, (intptr_t)&Ip4Interface, FKIOCTL, kcred, &ret);
+ if (!rc)
+ {
+ /*
+ * Get the multiplex file descriptor to the lower streams. Generally this is lost
+ * once a module is I_PLINK, we need to reobtain it for inserting/removing ourselves from the stack.
+ */
+ int Ip4MuxFd;
+ int ArpMuxFd;
+ rc = vboxNetFltSolarisMuxIdToFd(pUdp4VNode, Ip4Interface.lifr_ip_muxid, &Ip4MuxFd);
+ rc2 = vboxNetFltSolarisMuxIdToFd(pUdp4VNode, Ip4Interface.lifr_arp_muxid, &ArpMuxFd);
+ if ( RT_SUCCESS(rc)
+ && RT_SUCCESS(rc2))
+ {
+ /*
+ * We need to I_PUNLINK on these multiplexor IDs before we can start
+ * operating on the lower stream as insertions are direct operations on the lower stream.
+ */
+ rc = strioctl(pUdp4VNode, I_PUNLINK, (intptr_t)Ip4Interface.lifr_ip_muxid, 0, K_TO_K, kcred, &ret);
+ rc2 = strioctl(pUdp4VNode, I_PUNLINK, (intptr_t)Ip4Interface.lifr_arp_muxid, 0, K_TO_K, kcred, &ret);
+ if ( !rc
+ && !rc2)
+ {
+ /*
+ * Obtain the vnode from the useless userland file descriptor.
+ */
+ file_t *pIpFile = getf(Ip4MuxFd);
+ file_t *pArpFile = getf(ArpMuxFd);
+ if ( pIpFile
+ && pArpFile
+ && VNODE_FOR_FILE_T(pArpFile)
+ && VNODE_FOR_FILE_T(pIpFile))
+ {
+ vnode_t *pIp4VNode = VNODE_FOR_FILE_T(pIpFile);
+ vnode_t *pArpVNode = VNODE_FOR_FILE_T(pArpFile);
+
+ /*
+ * Find the position on the host stack for attaching/detaching ourselves.
+ */
+ rc = vboxNetFltSolarisDetermineModPos(fAttach, pIp4VNode, &StrMod.pos);
+ rc2 = vboxNetFltSolarisDetermineModPos(fAttach, pArpVNode, &ArpStrMod.pos);
+ if ( RT_SUCCESS(rc)
+ && RT_SUCCESS(rc2))
+ {
+ /*
+ * Inject/Eject from the host IP stack.
+ */
+
+ /*
+ * Set global data which will be grabbed by ModOpen.
+ * There is a known (though very unlikely) race here because
+ * of the inability to pass user data while inserting.
+ */
+ rc = RTSemFastMutexRequest(g_VBoxNetFltSolarisMtx);
+ AssertRCReturn(rc, rc);
+
+ if (fAttach)
+ {
+ g_VBoxNetFltSolarisInstance = pThis;
+ g_VBoxNetFltSolarisStreamType = kIp4Stream;
+ }
+
+ rc = strioctl(pIp4VNode, fAttach ? _I_INSERT : _I_REMOVE, (intptr_t)&StrMod, 0, K_TO_K,
+ g_pVBoxNetFltSolarisCred, &ret);
+
+ if (fAttach)
+ {
+ g_VBoxNetFltSolarisInstance = NULL;
+ g_VBoxNetFltSolarisStreamType = kUndefined;
+ }
+
+ RTSemFastMutexRelease(g_VBoxNetFltSolarisMtx);
+
+ if (!rc)
+ {
+ /*
+ * Inject/Eject from the host ARP stack.
+ */
+ rc = RTSemFastMutexRequest(g_VBoxNetFltSolarisMtx);
+ AssertRCReturn(rc, rc);
+
+ if (fAttach)
+ {
+ g_VBoxNetFltSolarisInstance = pThis;
+ g_VBoxNetFltSolarisStreamType = kArpStream;
+ }
+
+ rc = strioctl(pArpVNode, fAttach ? _I_INSERT : _I_REMOVE, (intptr_t)&ArpStrMod, 0, K_TO_K,
+ g_pVBoxNetFltSolarisCred, &ret);
+
+ if (fAttach)
+ {
+ g_VBoxNetFltSolarisInstance = NULL;
+ g_VBoxNetFltSolarisStreamType = kUndefined;
+ }
+
+ RTSemFastMutexRelease(g_VBoxNetFltSolarisMtx);
+
+ if (!rc)
+ {
+ /*
+ * Our job's not yet over; we need to relink the upper and lower streams
+ * otherwise we've pretty much screwed up the host interface.
+ */
+ rc = vboxNetFltSolarisRelinkIp4(pUdp4VNode, &Ip4Interface, Ip4MuxFd, ArpMuxFd);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Close the devices ONLY during the return from function case; otherwise
+ * we end up close twice which is an instant kernel panic.
+ */
+ vboxNetFltSolarisCloseDev(pUdp4VNodeHeld, pUdp4User);
+ ldi_close(ArpDevHandle, FREAD | FWRITE, kcred);
+ ldi_close(Ip4DevHandle, FREAD | FWRITE, kcred);
+ releasef(Ip4MuxFd);
+ releasef(ArpMuxFd);
+
+ Log((DEVICE_NAME ":vboxNetFltSolarisAttachIp4: Success! %s %s@(IPv4:%d Arp:%d) "
+ "%s interface %s\n", fAttach ? "Injected" : "Ejected", StrMod.mod_name,
+ StrMod.pos, ArpStrMod.pos, fAttach ? "to" : "from", pThis->szName));
+ return VINF_SUCCESS;
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp4: Relinking failed. Mode=%s rc=%d.\n",
+ fAttach ? "inject" : "eject", rc));
+ }
+
+ /*
+ * Try failing gracefully during attach.
+ */
+ if (fAttach)
+ strioctl(pArpVNode, _I_REMOVE, (intptr_t)&StrMod, 0, K_TO_K, kcred, &ret);
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp4: failed to %s the ARP stack. rc=%d\n",
+ fAttach ? "inject into" : "eject from", rc));
+ }
+
+ if (fAttach)
+ strioctl(pIp4VNode, _I_REMOVE, (intptr_t)&StrMod, 0, K_TO_K, kcred, &ret);
+
+ vboxNetFltSolarisRelinkIp4(pUdp4VNode, &Ip4Interface, Ip4MuxFd, ArpMuxFd);
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp4: failed to %s the IP stack. rc=%d\n",
+ fAttach ? "inject into" : "eject from", rc));
+ }
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp4: failed to find position. rc=%d rc2=%d\n", rc,
+ rc2));
+ }
+
+ releasef(Ip4MuxFd);
+ releasef(ArpMuxFd);
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp4: failed to get vnode from MuxFd.\n"));
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp4: failed to unlink upper stream rc=%d rc2=%d.\n", rc,
+ rc2));
+ }
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp4: failed to get MuxFd from MuxId. rc=%d rc2=%d\n", rc, rc2));
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp4: failed to get Mux Ids. rc=%d\n", rc));
+ vboxNetFltSolarisCloseDev(pUdp4VNodeHeld, pUdp4User);
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp4: failed to open UDP. rc=%d\n", rc));
+
+ rc = VERR_INTNET_FLT_IF_FAILED;
+ }
+ else
+ {
+ /*
+ * This would happen for interfaces that are not plumbed.
+ */
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp4: Warning: seems '%s' is unplumbed.\n", pThis->szName));
+ rc = VINF_SUCCESS;
+ }
+
+ ldi_close(ArpDevHandle, FREAD | FWRITE, kcred);
+ ldi_close(Ip4DevHandle, FREAD | FWRITE, kcred);
+
+ return rc;
+}
+
+
+/**
+ * Dynamically attach under IPv6 on the host stack.
+ *
+ * @returns VBox status code.
+ * @param pThis The instance.
+ * @param fAttach Is this an attach or detach.
+ */
+static int vboxNetFltSolarisAttachIp6(PVBOXNETFLTINS pThis, bool fAttach)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarisAttachIp6 pThis=%p fAttach=%d\n", pThis, fAttach));
+
+ /*
+ * Statutory Warning: Hackish code ahead.
+ */
+ char *pszModName = DEVICE_NAME;
+
+ struct lifreq Ip6Interface;
+ bzero(&Ip6Interface, sizeof(Ip6Interface));
+ Ip6Interface.lifr_addr.ss_family = AF_INET6;
+ strncpy(Ip6Interface.lifr_name, pThis->szName, sizeof(Ip6Interface.lifr_name));
+
+ struct strmodconf StrMod;
+ StrMod.mod_name = pszModName;
+ StrMod.pos = -1; /* this is filled in later. */
+
+ int rc;
+ int ret;
+ ldi_ident_t DeviceIdent = ldi_ident_from_anon();
+ ldi_handle_t Ip6DevHandle;
+
+ /*
+ * Open the IPv6 stream as a layered devices.
+ */
+ rc = ldi_open_by_name(IP6_DEV_NAME, FREAD | FWRITE, kcred, &Ip6DevHandle, DeviceIdent);
+ ldi_ident_release(DeviceIdent);
+ if (rc)
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp6: failed to open the IPv6 stream on '%s'.\n", pThis->szName));
+ return VERR_INTNET_FLT_IF_FAILED;
+ }
+
+ /*
+ * Obtain the interface flags from IPv6.
+ */
+ rc = vboxNetFltSolarisGetIfFlags(Ip6DevHandle, &Ip6Interface);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Open the UDP stream. We sort of cheat here and obtain the vnode so that we can perform
+ * things that are not possible from the layered interface.
+ */
+ vnode_t *pUdp6VNode = NULL;
+ vnode_t *pUdp6VNodeHeld = NULL;
+ TIUSER *pUdp6User = NULL;
+ rc = vboxNetFltSolarisOpenDev(UDP6_DEV_NAME, &pUdp6VNode, &pUdp6VNodeHeld, &pUdp6User);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Get the multiplexor IDs.
+ */
+ rc = ldi_ioctl(Ip6DevHandle, SIOCGLIFMUXID, (intptr_t)&Ip6Interface, FKIOCTL, kcred, &ret);
+ if (!rc)
+ {
+ /*
+ * Get the multiplex file descriptor to the lower streams. Generally this is lost
+ * once a module is I_PLINK, we need to reobtain it for inserting/removing ourselves from the stack.
+ */
+ int Ip6MuxFd;
+ rc = vboxNetFltSolarisMuxIdToFd(pUdp6VNode, Ip6Interface.lifr_ip_muxid, &Ip6MuxFd);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * We need to I_PUNLINK on these multiplexor IDs before we can start
+ * operating on the lower stream as insertions are direct operations on the lower stream.
+ */
+ rc = strioctl(pUdp6VNode, I_PUNLINK, (intptr_t)Ip6Interface.lifr_ip_muxid, 0, K_TO_K, kcred, &ret);
+ if (!rc)
+ {
+ /*
+ * Obtain the vnode from the useless userland file descriptor.
+ */
+ file_t *pIpFile = getf(Ip6MuxFd);
+ if ( pIpFile
+ && VNODE_FOR_FILE_T(pIpFile))
+ {
+ vnode_t *pIp6VNode = VNODE_FOR_FILE_T(pIpFile);
+
+ /*
+ * Find the position on the host stack for attaching/detaching ourselves.
+ */
+ rc = vboxNetFltSolarisDetermineModPos(fAttach, pIp6VNode, &StrMod.pos);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Set global data which will be grabbed by ModOpen.
+ * There is a known (though very unlikely) race here because
+ * of the inability to pass user data while inserting.
+ */
+ rc = RTSemFastMutexRequest(g_VBoxNetFltSolarisMtx);
+ AssertRCReturn(rc, rc);
+
+ if (fAttach)
+ {
+ g_VBoxNetFltSolarisInstance = pThis;
+ g_VBoxNetFltSolarisStreamType = kIp6Stream;
+ }
+
+ /*
+ * Inject/Eject from the host IPv6 stack.
+ */
+ rc = strioctl(pIp6VNode, fAttach ? _I_INSERT : _I_REMOVE, (intptr_t)&StrMod, 0, K_TO_K,
+ g_pVBoxNetFltSolarisCred, &ret);
+
+ if (fAttach)
+ {
+ g_VBoxNetFltSolarisInstance = NULL;
+ g_VBoxNetFltSolarisStreamType = kUndefined;
+ }
+
+ RTSemFastMutexRelease(g_VBoxNetFltSolarisMtx);
+
+ if (!rc)
+ {
+ /*
+ * Our job's not yet over; we need to relink the upper and lower streams
+ * otherwise we've pretty much screwed up the host interface.
+ */
+ rc = vboxNetFltSolarisRelinkIp6(pUdp6VNode, &Ip6Interface, Ip6MuxFd);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Close the devices ONLY during the return from function case; otherwise
+ * we end up close twice which is an instant kernel panic.
+ */
+ vboxNetFltSolarisCloseDev(pUdp6VNodeHeld, pUdp6User);
+ ldi_close(Ip6DevHandle, FREAD | FWRITE, kcred);
+ releasef(Ip6MuxFd);
+
+ Log((DEVICE_NAME ":vboxNetFltSolarisAttachIp6: Success! %s %s@(IPv6:%d) "
+ "%s interface %s\n", fAttach ? "Injected" : "Ejected", StrMod.mod_name,
+ StrMod.pos, fAttach ? "to" : "from", pThis->szName));
+ return VINF_SUCCESS;
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp6: Relinking failed. Mode=%s rc=%d.\n",
+ fAttach ? "inject" : "eject", rc));
+ }
+
+ if (fAttach)
+ strioctl(pIp6VNode, _I_REMOVE, (intptr_t)&StrMod, 0, K_TO_K, kcred, &ret);
+
+ vboxNetFltSolarisRelinkIp6(pUdp6VNode, &Ip6Interface, Ip6MuxFd);
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp6: failed to %s the IP stack. rc=%d\n",
+ fAttach ? "inject into" : "eject from", rc));
+ }
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp6: failed to find position. rc=%d\n", rc));
+
+ releasef(Ip6MuxFd);
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp6: failed to get vnode from MuxFd.\n"));
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp6: failed to unlink upper stream rc=%d.\n", rc));
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp6: failed to get MuxFd from MuxId. rc=%d\n", rc));
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp6: failed to get Mux Ids. rc=%d\n", rc));
+
+ vboxNetFltSolarisCloseDev(pUdp6VNodeHeld, pUdp6User);
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp6: failed to open UDP. rc=%d\n", rc));
+
+ rc = VERR_INTNET_FLT_IF_FAILED;
+ }
+ else
+ {
+ Log((DEVICE_NAME ":vboxNetFltSolarisAttachIp6: failed to get IPv6 flags.\n", pThis->szName));
+ rc = VERR_INTNET_FLT_IF_NOT_FOUND;
+ }
+
+ ldi_close(Ip6DevHandle, FREAD | FWRITE, kcred);
+
+ return rc;
+}
+
+
+#ifdef VBOXNETFLT_SOLARIS_IPV6_POLLING
+/**
+ * Ipv6 dynamic attachment timer callback to attach to the Ipv6 stream if needed.
+ *
+ * @param pTimer Pointer to the timer.
+ * @param pvData Opaque pointer to the instance.
+ * @param iTick Timer tick (unused).
+ */
+static void vboxNetFltSolarispIp6Timer(PRTTIMER pTimer, void *pvData, uint64_t iTick)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarispIp6Timer pTimer=%p pvData=%p\n", pTimer, pvData));
+
+ PVBOXNETFLTINS pThis = (PVBOXNETFLTINS)pvData;
+ if ( RT_LIKELY(pThis)
+ && RT_LIKELY(pTimer))
+ {
+ vboxnetflt_stream_t *pIp6Stream = ASMAtomicUoReadPtrT(&pThis->u.s.pIp6Stream, vboxnetflt_stream_t *);
+ bool fIp6Attaching = ASMAtomicUoReadBool(&pThis->u.s.fAttaching);
+ if ( !pIp6Stream
+ && !fIp6Attaching)
+ {
+ int rc = RTSemFastMutexRequest(pThis->u.s.hPollMtx);
+ if (RT_SUCCESS(rc))
+ {
+ ASMAtomicUoWriteBool(&pThis->u.s.fAttaching, true);
+
+ vboxNetFltSolarisAttachIp6(pThis, true /* fAttach */);
+
+ ASMAtomicUoWriteBool(&pThis->u.s.fAttaching, false);
+ RTSemFastMutexRelease(pThis->u.s.hPollMtx);
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarispIp6Timer failed to obtain mutex. rc=%Rrc\n", rc));
+ }
+ }
+
+ NOREF(iTick);
+}
+
+
+/**
+ * Setups up a kernel timer based on the driver property for attaching to IPv6 stream
+ * whenever the stream gets plumbed for the interface.
+ *
+ * @returns VBox status code.
+ * @param pThis The instance.
+ */
+static int vboxNetFltSolarisSetupIp6Polling(PVBOXNETFLTINS pThis)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarisSetupIp6Polling pThis=%p\n", pThis));
+
+ int rc = VERR_GENERAL_FAILURE;
+ vboxnetflt_promisc_stream_t *pPromiscStream = ASMAtomicUoReadPtrT(&pThis->u.s.pPromiscStream, vboxnetflt_promisc_stream_t *);
+ if (RT_LIKELY(pPromiscStream))
+ {
+ if (RT_LIKELY(pPromiscStream->pIp6Timer == NULL))
+ {
+ /*
+ * Validate IPv6 polling interval.
+ */
+ int Interval = g_VBoxNetFltSolarisPollInterval;
+ if (Interval < 1 || Interval > 120)
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisSetupIp6Polling: Invalid polling interval %d. Expected between"
+ " 1 and 120 secs.\n", Interval));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ /*
+ * Setup kernel poll timer.
+ */
+ rc = RTTimerCreateEx(&pPromiscStream->pIp6Timer, Interval * (uint64_t)1000000000, RTTIMER_FLAGS_CPU_ANY,
+ vboxNetFltSolarispIp6Timer, (void *)pThis);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTTimerStart(pPromiscStream->pIp6Timer, 10 * (uint64_t)1000000000 /* 10 seconds to blastoff */);
+ Log((DEVICE_NAME ":vboxNetFltSolarisSetupIp6Polling: Ipv6 %d second timer begins firing in 10 seconds.\n",
+ Interval));
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisSetupIp6Polling: Failed to create timer. rc=%d\n", rc));
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisSetupIp6Polling: Polling already started.\n"));
+ rc = VINF_SUCCESS;
+ }
+ }
+ return rc;
+}
+#endif
+
+/**
+ * Wrapper for detaching ourselves from the interface.
+ *
+ * @returns VBox status code.
+ * @param pThis The instance.
+ * @remarks Owns the globals mutex, so re-requesting it anytime during this phase
+ * would panic the system (e.g. in vboxNetFltSolarisFindInstance).
+ */
+static int vboxNetFltSolarisDetachFromInterface(PVBOXNETFLTINS pThis)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarisDetachFromInterface pThis=%p\n", pThis));
+
+ ASMAtomicWriteBool(&pThis->fDisconnectedFromHost, true);
+ vboxNetFltSolarisCloseStream(pThis);
+ int rc = VINF_SUCCESS;
+ if (pThis->u.s.pIp4Stream)
+ rc = vboxNetFltSolarisAttachIp4(pThis, false /* fAttach */);
+ if (pThis->u.s.pIp6Stream)
+ rc = vboxNetFltSolarisAttachIp6(pThis, false /* fAttach */);
+
+#ifdef VBOXNETFLT_SOLARIS_IPV6_POLLING
+ vboxnetflt_promisc_stream_t *pPromiscStream = ASMAtomicUoReadPtrT(&pThis->u.s.pPromiscStream, vboxnetflt_promisc_stream_t *);
+ if ( pPromiscStream
+ && pPromiscStream->pIp6Timer == NULL)
+ {
+ RTTimerStop(pPromiscStream->pIp6Timer);
+ RTTimerDestroy(pPromiscStream->pIp6Timer);
+ ASMAtomicUoWriteNullPtr(&pPromiscStream->pIp6Timer);
+ }
+#endif
+
+ return rc;
+}
+
+
+/**
+ * Wrapper for attaching ourselves to the interface.
+ *
+ * @returns VBox status code.
+ * @param pThis The instance.
+ */
+static int vboxNetFltSolarisAttachToInterface(PVBOXNETFLTINS pThis)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarisAttachToInterface pThis=%p\n", pThis));
+
+ /*
+ * Since this is asynchronous streams injection, let the attach succeed before we can start
+ * processing the stream.
+ */
+ ASMAtomicWriteBool(&pThis->fDisconnectedFromHost, true);
+ int rc = vboxNetFltSolarisOpenStream(pThis);
+ if (RT_SUCCESS(rc))
+ {
+ rc = vboxNetFltSolarisAttachIp4(pThis, true /* fAttach */);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Ipv6 attaching is optional and can fail. We don't bother to bring down the whole
+ * attach process just if Ipv6 interface is unavailable.
+ */
+ int rc2 = vboxNetFltSolarisAttachIp6(pThis, true /* fAttach */);
+
+#ifdef VBOXNETFLT_SOLARIS_IPV6_POLLING
+ /*
+ * If Ip6 interface is not plumbed and an Ip6 polling interval is specified, we need
+ * to begin polling to attach on the Ip6 interface whenever it comes up.
+ */
+ if ( rc2 == VERR_INTNET_FLT_IF_NOT_FOUND
+ && g_VBoxNetFltSolarisPollInterval != -1)
+ {
+ int rc3 = vboxNetFltSolarisSetupIp6Polling(pThis);
+ if (RT_FAILURE(rc3))
+ {
+ /*
+ * If we failed to setup Ip6 polling, warn in the release log and continue.
+ */
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachToInterface IPv6 polling inactive. rc=%Rrc\n", rc3));
+ }
+ }
+#endif
+
+ /*
+ * Report promiscuousness and capabilities.
+ */
+ if (vboxNetFltTryRetainBusyNotDisconnected(pThis))
+ {
+ Assert(pThis->pSwitchPort);
+ /** @todo There is no easy way of obtaining the global host side promiscuous
+ * counter. Currently we just return false. */
+ pThis->pSwitchPort->pfnReportPromiscuousMode(pThis->pSwitchPort, false);
+ pThis->pSwitchPort->pfnReportGsoCapabilities(pThis->pSwitchPort, 0, INTNETTRUNKDIR_WIRE | INTNETTRUNKDIR_HOST);
+ pThis->pSwitchPort->pfnReportNoPreemptDsts(pThis->pSwitchPort, 0 /* none */);
+ vboxNetFltRelease(pThis, true /*fBusy*/);
+ }
+
+ /*
+ * Ipv4 is successful, and maybe Ipv6, we're ready for transfers.
+ */
+ ASMAtomicWriteBool(&pThis->fDisconnectedFromHost, false);
+
+ return VINF_SUCCESS;
+ }
+
+ vboxNetFltSolarisCloseStream(pThis);
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachToInterface vboxNetFltSolarisOpenStream failed rc=%Rrc\n", rc));
+
+ return rc;
+}
+
+
+/**
+ * Create a solaris message block from the SG list.
+ *
+ * @returns Solaris message block.
+ * @param pThis The instance.
+ * @param pSG Pointer to the scatter-gather list.
+ * @param fDst The destination mask, INTNETTRUNKDIR_XXX. Ignored.
+ */
+static mblk_t *vboxNetFltSolarisMBlkFromSG(PVBOXNETFLTINS pThis, PINTNETSG pSG, uint32_t fDst)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarisMBlkFromSG pThis=%p pSG=%p\n", pThis, pSG));
+
+ mblk_t *pMsg = allocb(pSG->cbTotal, BPRI_MED);
+ if (RT_UNLIKELY(!pMsg))
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisMBlkFromSG failed to alloc %d bytes for mblk_t.\n", pSG->cbTotal));
+ return NULL;
+ }
+
+ /*
+ * Single buffer copy. Maybe later explore the
+ * need/possibility for using a mblk_t chain rather.
+ */
+ for (unsigned i = 0; i < pSG->cSegsUsed; i++)
+ {
+ if (pSG->aSegs[i].pv)
+ {
+ bcopy(pSG->aSegs[i].pv, pMsg->b_wptr, pSG->aSegs[i].cb);
+ pMsg->b_wptr += pSG->aSegs[i].cb;
+ }
+ }
+ DB_TYPE(pMsg) = M_DATA;
+ return pMsg;
+}
+
+
+/**
+ * Calculate the number of segments required for this message block.
+ *
+ * @returns Number of segments.
+ * @param pThis The instance
+ * @param pMsg Pointer to the data message.
+ */
+static unsigned vboxNetFltSolarisMBlkCalcSGSegs(PVBOXNETFLTINS pThis, mblk_t *pMsg)
+{
+ unsigned cSegs = 0;
+ for (mblk_t *pCur = pMsg; pCur; pCur = pCur->b_cont)
+ if (MBLKL(pCur))
+ cSegs++;
+
+#ifdef PADD_RUNT_FRAMES_FROM_HOST
+ if (msgdsize(pMsg) < 60)
+ cSegs++;
+#endif
+
+ NOREF(pThis);
+ return RT_MAX(cSegs, 1);
+}
+
+
+/**
+ * Initializes an SG list from the given message block.
+ *
+ * @returns VBox status code.
+ * @param pThis The instance.
+ * @param pMsg Pointer to the data message.
+ The caller must ensure it's not a control message block.
+ * @param pSG Pointer to the SG.
+ * @param cSegs Number of segments in the SG.
+ * This should match the number in the message block exactly!
+ * @param fSrc The source of the message.
+ */
+static int vboxNetFltSolarisMBlkToSG(PVBOXNETFLTINS pThis, mblk_t *pMsg, PINTNETSG pSG, unsigned cSegs, uint32_t fSrc)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarisMBlkToSG pThis=%p pMsg=%p pSG=%p cSegs=%d\n", pThis, pMsg, pSG, cSegs));
+
+ /*
+ * Convert the message block to segments. Work INTNETSG::cbTotal.
+ */
+ IntNetSgInitTempSegs(pSG, 0 /*cbTotal*/, cSegs, 0 /*cSegsUsed*/);
+ mblk_t *pCur = pMsg;
+ unsigned iSeg = 0;
+ while (pCur)
+ {
+ size_t cbSeg = MBLKL(pCur);
+ if (cbSeg)
+ {
+ void *pvSeg = pCur->b_rptr;
+ pSG->aSegs[iSeg].pv = pvSeg;
+ pSG->aSegs[iSeg].cb = cbSeg;
+ pSG->aSegs[iSeg].Phys = NIL_RTHCPHYS;
+ pSG->cbTotal += cbSeg;
+ iSeg++;
+ }
+ pCur = pCur->b_cont;
+ }
+ pSG->cSegsUsed = iSeg;
+
+#ifdef PADD_RUNT_FRAMES_FROM_HOST
+ if (pSG->cbTotal < 60 && (fSrc & INTNETTRUNKDIR_HOST))
+ {
+ Log((DEVICE_NAME ":vboxNetFltSolarisMBlkToSG pulling up to length.\n"));
+
+ static uint8_t const s_abZero[128] = {0};
+ pSG->aSegs[iSeg].Phys = NIL_RTHCPHYS;
+ pSG->aSegs[iSeg].pv = (void *)&s_abZero[0];
+ pSG->aSegs[iSeg].cb = 60 - pSG->cbTotal;
+ pSG->cbTotal = 60;
+ pSG->cSegsUsed++;
+ Assert(iSeg + 1 < cSegs);
+ }
+#endif
+
+ Log((DEVICE_NAME ":vboxNetFltSolarisMBlkToSG iSeg=%d pSG->cbTotal=%d msgdsize=%d\n", iSeg, pSG->cbTotal, msgdsize(pMsg)));
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Converts raw mode M_DATA messages to M_PROTO DL_UNITDATA_IND format.
+ *
+ * @returns VBox status code.
+ * @param pMsg Pointer to the raw message.
+ * @param ppDlpiMsg Where to store the M_PROTO message.
+ *
+ * @remarks The original raw message would be no longer valid and will be
+ * linked as part of the new DLPI message. Callers must take care
+ * not to use the raw message if this routine is successful.
+ */
+static int vboxNetFltSolarisRawToUnitData(mblk_t *pMsg, mblk_t **ppDlpiMsg)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarisRawToUnitData pMsg=%p\n", pMsg));
+
+ if (DB_TYPE(pMsg) != M_DATA)
+ return VERR_NO_MEMORY;
+
+ size_t cbMsg = sizeof(dl_unitdata_ind_t) + 2 * sizeof(vboxnetflt_dladdr_t);
+ mblk_t *pDlpiMsg = allocb(cbMsg, BPRI_MED);
+ if (RT_UNLIKELY(!pDlpiMsg))
+ return VERR_NO_MEMORY;
+
+ DB_TYPE(pDlpiMsg) = M_PROTO;
+ dl_unitdata_ind_t *pDlpiData = (dl_unitdata_ind_t *)pDlpiMsg->b_rptr;
+ pDlpiData->dl_primitive = DL_UNITDATA_IND;
+ pDlpiData->dl_dest_addr_length = VBOXNETFLT_DLADDRL;
+ pDlpiData->dl_dest_addr_offset = sizeof(dl_unitdata_ind_t);
+ pDlpiData->dl_src_addr_length = VBOXNETFLT_DLADDRL;
+ pDlpiData->dl_src_addr_offset = VBOXNETFLT_DLADDRL + sizeof(dl_unitdata_ind_t);
+
+ PRTNETETHERHDR pEthHdr = (PRTNETETHERHDR)pMsg->b_rptr;
+
+ vboxnetflt_dladdr_t *pDlAddr = (vboxnetflt_dladdr_t *)(pDlpiMsg->b_rptr + pDlpiData->dl_dest_addr_offset);
+ pDlAddr->SAP = RT_BE2H_U16(pEthHdr->EtherType);
+ bcopy(&pEthHdr->DstMac, &pDlAddr->Mac, sizeof(RTMAC));
+
+ pDlAddr = (vboxnetflt_dladdr_t *)(pDlpiMsg->b_rptr + pDlpiData->dl_src_addr_offset);
+ pDlAddr->SAP = RT_BE2H_U16(pEthHdr->EtherType);
+ bcopy(&pEthHdr->SrcMac, &pDlAddr->Mac, sizeof(RTMAC));
+
+ pDlpiMsg->b_wptr = pDlpiMsg->b_rptr + cbMsg;
+
+ /* Make the message point to the protocol header */
+ pMsg->b_rptr += sizeof(RTNETETHERHDR);
+
+ pDlpiMsg->b_cont = pMsg;
+ *ppDlpiMsg = pDlpiMsg;
+ return VINF_SUCCESS;
+}
+
+#if 0
+/**
+ * Converts DLPI M_PROTO messages to the raw mode M_DATA format.
+ *
+ * @returns VBox status code.
+ * @param pMsg Pointer to the M_PROTO message.
+ * @param ppRawMsg Where to store the converted message.
+ *
+ * @remarks If successful, the original pMsg is no longer valid, it will be deleted.
+ * Callers must take care not to continue to use pMsg after a successful
+ * call to this conversion routine.
+ */
+static int vboxNetFltSolarisUnitDataToRaw(PVBOXNETFLTINS pThis, mblk_t *pMsg, mblk_t **ppRawMsg)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarisUnitDataToRaw pMsg=%p\n", pMsg));
+
+ if ( !pMsg->b_cont
+ || DB_TYPE(pMsg) != M_PROTO)
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisUnitDataToRaw invalid input message.\n"));
+ return VERR_NET_PROTOCOL_ERROR;
+ }
+
+ /*
+ * Upstream consumers send/receive packets in the fast path mode.
+ * We of course need to convert them into raw ethernet frames.
+ */
+ RTNETETHERHDR EthHdr;
+ union DL_primitives *pPrim = (union DL_primitives *)pMsg->b_rptr;
+ switch (pPrim->dl_primitive)
+ {
+ case DL_UNITDATA_IND:
+ {
+ /*
+ * Receive side.
+ */
+ dl_unitdata_ind_t *pDlpiMsg = (dl_unitdata_ind_t *)pMsg->b_rptr;
+ bcopy(pMsg->b_rptr + pDlpiMsg->dl_dest_addr_offset, &EthHdr.DstMac, sizeof(EthHdr.DstMac));
+ bcopy(pMsg->b_rptr + pDlpiMsg->dl_src_addr_offset, &EthHdr.SrcMac, sizeof(EthHdr.SrcMac));
+
+ vboxnetflt_dladdr_t *pDLSapAddr = (vboxnetflt_dladdr_t *)(pMsg->b_rptr + pDlpiMsg->dl_dest_addr_offset);
+ EthHdr.EtherType = RT_H2BE_U16(pDLSapAddr->SAP);
+
+ break;
+ }
+
+ case DL_UNITDATA_REQ:
+ {
+ /*
+ * Send side.
+ */
+ dl_unitdata_req_t *pDlpiMsg = (dl_unitdata_req_t *)pMsg->b_rptr;
+
+ bcopy(pMsg->b_rptr + pDlpiMsg->dl_dest_addr_offset, &EthHdr.DstMac, sizeof(EthHdr.DstMac));
+ bcopy(&pThis->u.s.MacAddr, &EthHdr.SrcMac, sizeof(EthHdr.SrcMac));
+
+ vboxnetflt_dladdr_t *pDLSapAddr = (vboxnetflt_dladdr_t *)(pMsg->b_rptr + pDlpiMsg->dl_dest_addr_offset);
+ EthHdr.EtherType = RT_H2BE_U16(pDLSapAddr->SAP);
+
+ break;
+ }
+
+ default:
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisUnitDataToRaw Unknown M_PROTO. This shouldn't be happening!!"));
+ return VERR_NET_PROTOCOL_ERROR;
+ }
+ }
+
+ /*
+ * Let us just link it as a mblk_t chain rather than re-copy the entire message.
+ * The vboxNetFltSolarisMBlkToSG function will handle chained mblk_t's.
+ */
+ size_t cbLen = sizeof(EthHdr);
+ mblk_t *pEtherMsg = allocb(cbLen, BPRI_MED);
+ if (RT_UNLIKELY(!pEtherMsg))
+ return VERR_NO_MEMORY;
+
+ DB_TYPE(pEtherMsg) = M_DATA;
+ bcopy(&EthHdr, pEtherMsg->b_wptr, sizeof(EthHdr));
+ pEtherMsg->b_wptr += cbLen;
+
+ pEtherMsg->b_cont = pMsg->b_cont;
+
+ /*
+ * Change the chained blocks to type M_DATA.
+ */
+ for (mblk_t *pTmp = pEtherMsg->b_cont; pTmp; pTmp = pTmp->b_cont)
+ DB_TYPE(pTmp) = M_DATA;
+
+ pMsg->b_cont = NULL;
+ freemsg(pMsg);
+
+ *ppRawMsg = pEtherMsg;
+ return VINF_SUCCESS;
+}
+#endif
+
+/**
+ * Initializes a packet identifier.
+ *
+ * @param pTag Pointer to the packed identifier.
+ * @param pMsg Pointer to the message to be identified.
+ *
+ * @remarks Warning!!! This function assumes 'pMsg' is an unchained message.
+ */
+static inline void vboxNetFltSolarisInitPacketId(PVBOXNETFLTPACKETID pTag, mblk_t *pMsg)
+{
+ PCRTNETETHERHDR pEthHdr = (PCRTNETETHERHDR)pMsg->b_rptr;
+ size_t cbMsg = MBLKL(pMsg);
+
+ pTag->cbPacket = cbMsg;
+ pTag->Checksum = RTCrc32(pMsg->b_rptr, cbMsg);
+ bcopy(&pEthHdr->SrcMac, &pTag->SrcMac, sizeof(RTMAC));
+ bcopy(&pEthHdr->DstMac, &pTag->DstMac, sizeof(RTMAC));
+}
+
+
+/**
+ * Queues a packet for loopback elimination.
+ *
+ * @returns VBox status code.
+ * @param pThis The instance.
+ * @param pPromiscStream Pointer to the promiscuous stream.
+ * @param pMsg Pointer to the message.
+ */
+static int vboxNetFltSolarisQueueLoopback(PVBOXNETFLTINS pThis, vboxnetflt_promisc_stream_t *pPromiscStream, mblk_t *pMsg)
+{
+ Assert(pThis);
+ Assert(pMsg);
+ Assert(DB_TYPE(pMsg) == M_DATA);
+ Assert(pPromiscStream);
+
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarisQueueLoopback pThis=%p pPromiscStream=%p pMsg=%p\n", pThis, pPromiscStream, pMsg));
+
+ if (RT_UNLIKELY(pMsg->b_cont))
+ {
+ /*
+ * We don't currently make chained messages in on Xmit
+ * so this only needs to be supported when we do that.
+ */
+ return VERR_NOT_SUPPORTED;
+ }
+
+ size_t cbMsg = MBLKL(pMsg);
+ if (RT_UNLIKELY(cbMsg < sizeof(RTNETETHERHDR)))
+ return VERR_NET_MSG_SIZE;
+
+ int rc = VINF_SUCCESS;
+ mutex_enter(&pThis->u.s.hMtx);
+
+ PVBOXNETFLTPACKETID pCur = NULL;
+ if (pPromiscStream->cLoopback < VBOXNETFLT_LOOPBACK_SIZE
+ || ( pPromiscStream->pHead
+ && pPromiscStream->pHead->cbPacket == 0))
+ {
+ do
+ {
+ if (!pPromiscStream->pHead)
+ {
+ pCur = RTMemAlloc(sizeof(VBOXNETFLTPACKETID));
+ if (RT_UNLIKELY(!pCur))
+ {
+ rc = VERR_NO_MEMORY;
+ break;
+ }
+
+ vboxNetFltSolarisInitPacketId(pCur, pMsg);
+
+ pCur->pNext = NULL;
+ pPromiscStream->pHead = pCur;
+ pPromiscStream->pTail = pCur;
+ pPromiscStream->cLoopback++;
+
+ Log((DEVICE_NAME ":vboxNetFltSolarisQueueLoopback initialized head. checksum=%u.\n",
+ pPromiscStream->pHead->Checksum));
+ break;
+ }
+ else if ( pPromiscStream->pHead
+ && pPromiscStream->pHead->cbPacket == 0)
+ {
+ pCur = pPromiscStream->pHead;
+ vboxNetFltSolarisInitPacketId(pCur, pMsg);
+
+ Log((DEVICE_NAME ":vboxNetFltSolarisQueueLoopback re-used head checksum=%u cLoopback=%d.\n",
+ pCur->Checksum, pPromiscStream->cLoopback));
+ break;
+ }
+ else
+ {
+ pCur = RTMemAlloc(sizeof(VBOXNETFLTPACKETID));
+ if (RT_UNLIKELY(!pCur))
+ {
+ rc = VERR_NO_MEMORY;
+ break;
+ }
+
+ vboxNetFltSolarisInitPacketId(pCur, pMsg);
+
+ pCur->pNext = pPromiscStream->pHead;
+ pPromiscStream->pHead = pCur;
+ pPromiscStream->cLoopback++;
+
+ Log((DEVICE_NAME ":vboxNetFltSolarisQueueLoopback added head checksum=%u cLoopback=%d.\n", pCur->Checksum,
+ pPromiscStream->cLoopback));
+ break;
+ }
+ } while (0);
+ }
+ else
+ {
+ /*
+ * Maximum loopback queue size reached. Re-use tail as head.
+ */
+ Assert(pPromiscStream->pHead);
+ Assert(pPromiscStream->pTail);
+
+ /*
+ * Find tail's previous item.
+ */
+ PVBOXNETFLTPACKETID pPrev = NULL;
+ pCur = pPromiscStream->pHead;
+
+ /** @todo consider if this is worth switching to a double linked list... */
+ while (pCur != pPromiscStream->pTail)
+ {
+ pPrev = pCur;
+ pCur = pCur->pNext;
+ }
+
+ pPromiscStream->pTail = pPrev;
+ pPromiscStream->pTail->pNext = NULL;
+ pCur->pNext = pPromiscStream->pHead;
+ pPromiscStream->pHead = pCur;
+
+ vboxNetFltSolarisInitPacketId(pCur, pMsg);
+ Log((DEVICE_NAME ":vboxNetFltSolarisQueueLoopback recycled tail!! checksum=%u cLoopback=%d\n", pCur->Checksum,
+ pPromiscStream->cLoopback));
+ }
+
+ mutex_exit(&pThis->u.s.hMtx);
+
+ return rc;
+}
+
+
+/**
+ * Checks if the packet is enqueued for loopback as our own packet.
+ *
+ * @returns If it's our packet, returns true after dequeuing it, otherwise false.
+ * @param pThis The instance.
+ * @param pPromiscStream Pointer to the promiscuous stream.
+ * @param pMsg Pointer to the message.
+ */
+static bool vboxNetFltSolarisIsOurMBlk(PVBOXNETFLTINS pThis, vboxnetflt_promisc_stream_t *pPromiscStream, mblk_t *pMsg)
+{
+ Assert(pThis);
+ Assert(pPromiscStream);
+ Assert(pMsg);
+ Assert(DB_TYPE(pMsg) == M_DATA);
+
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarisIsOurMBlk pThis=%p pMsg=%p\n", pThis, pMsg));
+
+ if (pMsg->b_cont)
+ {
+ /** Handle this when Xmit makes chained messages */
+ return false;
+ }
+
+ size_t cbMsg = MBLKL(pMsg);
+ if (cbMsg < sizeof(RTNETETHERHDR))
+ return false;
+
+ mutex_enter(&pThis->u.s.hMtx);
+
+ PVBOXNETFLTPACKETID pPrev = NULL;
+ PVBOXNETFLTPACKETID pCur = pPromiscStream->pHead;
+ bool fIsOurPacket = false;
+ while (pCur)
+ {
+ PCRTNETETHERHDR pEthHdr = (PCRTNETETHERHDR)pMsg->b_rptr;
+ if ( pCur->cbPacket != cbMsg
+ || pCur->SrcMac.au8[0] != pEthHdr->SrcMac.au8[0]
+ || pCur->SrcMac.au8[1] != pEthHdr->SrcMac.au8[1]
+ || pCur->SrcMac.au8[2] != pEthHdr->SrcMac.au8[2]
+ || pCur->SrcMac.au8[3] != pEthHdr->SrcMac.au8[3]
+ || pCur->SrcMac.au8[4] != pEthHdr->SrcMac.au8[4]
+ || pCur->SrcMac.au8[5] != pEthHdr->SrcMac.au8[5]
+ || pCur->DstMac.au8[0] != pEthHdr->DstMac.au8[0]
+ || pCur->DstMac.au8[1] != pEthHdr->DstMac.au8[1]
+ || pCur->DstMac.au8[2] != pEthHdr->DstMac.au8[2]
+ || pCur->DstMac.au8[3] != pEthHdr->DstMac.au8[3]
+ || pCur->DstMac.au8[4] != pEthHdr->DstMac.au8[4]
+ || pCur->DstMac.au8[5] != pEthHdr->DstMac.au8[5])
+ {
+ pPrev = pCur;
+ pCur = pCur->pNext;
+ continue;
+ }
+
+ uint16_t Checksum = RTCrc32(pMsg->b_rptr, cbMsg);
+ if (pCur->Checksum != Checksum)
+ {
+ pPrev = pCur;
+ pCur = pCur->pNext;
+ continue;
+ }
+
+ /*
+ * Yes, it really is our own packet, mark it as handled
+ * and move it as a "free slot" to the head and return success.
+ */
+ pCur->cbPacket = 0;
+ if (pPrev)
+ {
+ if (!pCur->pNext)
+ pPromiscStream->pTail = pPrev;
+
+ pPrev->pNext = pCur->pNext;
+ pCur->pNext = pPromiscStream->pHead;
+ pPromiscStream->pHead = pCur;
+ }
+ fIsOurPacket = true;
+
+ Log((DEVICE_NAME ":vboxNetFltSolarisIsOurMBlk found packet %p Checksum=%u cLoopback=%d\n", pMsg, Checksum,
+ pPromiscStream->cLoopback));
+ break;
+ }
+
+ Log((DEVICE_NAME ":vboxNetFltSolarisIsOurMBlk returns %d.\n", fIsOurPacket));
+ mutex_exit(&pThis->u.s.hMtx);
+ return fIsOurPacket;
+}
+
+
+/**
+ * Helper.
+ */
+DECLINLINE(bool) vboxNetFltPortSolarisIsHostMac(PVBOXNETFLTINS pThis, PCRTMAC pMac)
+{
+ /*
+ * MAC address change acknowledgements are intercepted on the read side
+ * hence theoretically we are always update to date with any changes.
+ */
+ return pThis->u.s.MacAddr.au16[0] == pMac->au16[0]
+ && pThis->u.s.MacAddr.au16[1] == pMac->au16[1]
+ && pThis->u.s.MacAddr.au16[2] == pMac->au16[2];
+}
+
+
+/**
+ * Worker for routing messages from the wire or from the host.
+ *
+ * @returns VBox status code.
+ * @param pThis The instance.
+ * @param pStream Pointer to the stream.
+ * @param pQueue Pointer to the read queue.
+ * @param pMsg Pointer to the message.
+ */
+static int vboxNetFltSolarisRecv(PVBOXNETFLTINS pThis, vboxnetflt_stream_t *pStream, queue_t *pQueue, mblk_t *pMsg)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarisRecv pThis=%p pMsg=%p\n", pThis, pMsg));
+
+ AssertCompile(sizeof(struct ether_header) == sizeof(RTNETETHERHDR));
+ Assert(pStream->Type == kPromiscStream);
+
+ vboxnetflt_promisc_stream_t *pPromiscStream = ASMAtomicUoReadPtrT(&pThis->u.s.pPromiscStream, vboxnetflt_promisc_stream_t *);
+ if (RT_UNLIKELY(!pPromiscStream))
+ {
+ LogRel((DEVICE_NAME ":Promiscuous stream missing!! Failing to receive packet.\n"));
+ return VERR_INVALID_POINTER;
+ }
+
+ /*
+ * Paranoia...
+ */
+ if (RT_UNLIKELY(MBLKL(pMsg) < sizeof(RTNETETHERHDR)))
+ {
+ size_t cbMsg = msgdsize(pMsg);
+ if (cbMsg < sizeof(RTNETETHERHDR))
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisRecv %s: packet too small. Dropping packet.\n", pThis->szName));
+ return VINF_SUCCESS;
+ }
+
+ mblk_t *pFullMsg = msgpullup(pMsg, -1 /* all data blocks */);
+ if (pFullMsg)
+ {
+ freemsg(pMsg);
+ pMsg = pFullMsg;
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisRecv msgpullup failed.\n"));
+ return VERR_NO_MEMORY;
+ }
+ }
+
+ /*
+ * Don't loopback packets we transmit to the wire.
+ */
+ if (vboxNetFltSolarisIsOurMBlk(pThis, pPromiscStream, pMsg))
+ {
+ Log((DEVICE_NAME ":Avoiding packet loopback.\n"));
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Figure out the source of the packet based on the source Mac address.
+ */
+ uint32_t fSrc = INTNETTRUNKDIR_WIRE;
+ PRTNETETHERHDR pEthHdr = (PRTNETETHERHDR)pMsg->b_rptr;
+ if (vboxNetFltPortSolarisIsHostMac(pThis, &pEthHdr->SrcMac))
+ fSrc = INTNETTRUNKDIR_HOST;
+
+ /*
+ * Afaik; we no longer need to worry about incorrect checksums because we now use
+ * a dedicated stream and don't intercept packets under IP/ARP which might be doing
+ * checksum offloading.
+ */
+#if 0
+ if (fSrc & INTNETTRUNKDIR_HOST)
+ {
+ mblk_t *pCorrectedMsg = vboxNetFltSolarisFixChecksums(pMsg);
+ if (pCorrectedMsg)
+ pMsg = pCorrectedMsg;
+ }
+ vboxNetFltSolarisAnalyzeMBlk(pMsg);
+#endif
+
+ /*
+ * Solaris raw mode streams for priority-tagged VLAN does not strip the VLAN tag.
+ * It zero's the VLAN-Id but keeps the tag intact as part of the Ethernet header.
+ * We need to manually strip these tags out or the guests might get confused.
+ */
+ bool fCopied = false;
+ bool fTagged = false;
+ if ( pThis->u.s.fVLAN
+ && pPromiscStream->fRawMode)
+ {
+ if (pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_VLAN))
+ {
+ if (msgdsize(pMsg) > sizeof(RTNETETHERHDR) + sizeof(VLANHEADER))
+ {
+ if (pMsg->b_cont)
+ {
+ mblk_t *pFullMsg = msgpullup(pMsg, -1 /* all data blocks */);
+ if (pFullMsg)
+ {
+ /* Original pMsg will be freed by the caller */
+ pMsg = pFullMsg;
+ fCopied = true;
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisRecv msgpullup failed.\n"));
+ return VERR_NO_MEMORY;
+ }
+ }
+
+ PVLANHEADER pVlanHdr = (PVLANHEADER)(pMsg->b_rptr + sizeof(RTNETETHERHDR) - sizeof(pEthHdr->EtherType));
+ Log((DEVICE_NAME ":Recv VLAN Pcp=%u Cfi=%u Id=%u\n", VLAN_PRI(RT_BE2H_U16(pVlanHdr->Data)),
+ VLAN_CFI(RT_BE2H_U16(pVlanHdr->Data)), VLAN_ID(RT_BE2H_U16(pVlanHdr->Data))));
+ if ( VLAN_PRI(RT_BE2H_U16(pVlanHdr->Data)) > 0
+ && VLAN_ID(RT_BE2H_U16(pVlanHdr->Data)) == 0)
+ {
+ /*
+ * Create new Ethernet header with stripped VLAN tag.
+ */
+ size_t cbEthPrefix = sizeof(RTNETETHERHDR) - sizeof(pEthHdr->EtherType);
+ mblk_t *pStrippedMsg = allocb(cbEthPrefix, BPRI_MED);
+ if (RT_LIKELY(pStrippedMsg))
+ {
+ fTagged = true;
+
+ /*
+ * Copy ethernet header excluding the ethertype.
+ */
+ bcopy(pMsg->b_rptr, pStrippedMsg->b_wptr, cbEthPrefix);
+ pStrippedMsg->b_wptr += cbEthPrefix;
+
+ /*
+ * Link the rest of the message (ethertype + data, skipping VLAN header).
+ */
+ pMsg->b_rptr += cbEthPrefix + sizeof(VLANHEADER);
+ pStrippedMsg->b_cont = pMsg;
+ pMsg = pStrippedMsg;
+ Log((DEVICE_NAME ":Stripped VLAN tag.\n"));
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisRecv insufficient memory for creating VLAN stripped packet"
+ " cbMsg=%u.\n", cbEthPrefix));
+ if (fCopied)
+ freemsg(pMsg);
+ return VERR_NO_MEMORY;
+ }
+ }
+ }
+ }
+ }
+
+ /*
+ * Route all received packets into the internal network.
+ */
+ unsigned cSegs = vboxNetFltSolarisMBlkCalcSGSegs(pThis, pMsg);
+ PINTNETSG pSG = (PINTNETSG)alloca(RT_UOFFSETOF_DYN(INTNETSG, aSegs[cSegs]));
+ int rc = vboxNetFltSolarisMBlkToSG(pThis, pMsg, pSG, cSegs, fSrc);
+ if (RT_SUCCESS(rc))
+ pThis->pSwitchPort->pfnRecv(pThis->pSwitchPort, NULL /* pvIf */, pSG, fSrc);
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisMBlkToSG failed. rc=%d\n", rc));
+
+ /*
+ * If we've allocated the prefix before the VLAN tag in a new message, free that.
+ */
+ if (fTagged)
+ {
+ mblk_t *pTagMsg = pMsg->b_cont;
+ pMsg->b_cont = NULL; /* b_cont could be the message from the caller or a copy we made (fCopied) */
+ freemsg(pMsg);
+ pMsg = pTagMsg;
+ }
+
+ /*
+ * If we made an extra copy for VLAN stripping, we need to free that ourselves.
+ */
+ if (fCopied)
+ freemsg(pMsg);
+
+ return VINF_SUCCESS;
+}
+
+#if 0
+/**
+ * Finalize the message to be fed into the internal network.
+ * Verifies and tries to fix checksums for TCP, UDP and IP.
+ *
+ * @returns Corrected message or NULL if no change was required.
+ * @param pMsg Pointer to the message block.
+ * This must not be DLPI linked messages, must be M_DATA.
+ *
+ * @remarks If this function returns a checksum adjusted message, the
+ * passed in input message has been freed and should not be
+ * referenced anymore by the caller.
+ */
+static mblk_t *vboxNetFltSolarisFixChecksums(mblk_t *pMsg)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarisFixChecksums pMsg=%p\n"));
+
+ Assert(DB_TYPE(pMsg) == M_DATA);
+
+ if (MBLKL(pMsg) < sizeof(RTNETETHERHDR))
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisFixChecksums Packet shorter than ethernet header size!\n"));
+ return NULL;
+ }
+
+ PRTNETETHERHDR pEthHdr = (PRTNETETHERHDR)pMsg->b_rptr;
+ if (pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_IPV4))
+ {
+ /*
+ * Check if we have a complete packet or being fed a chain.
+ */
+ size_t cbIpPacket = 0;
+ mblk_t *pFullMsg = NULL;
+ if (pMsg->b_cont)
+ {
+ Log((DEVICE_NAME ":Chained mblk_t.\n"));
+
+ /*
+ * Handle chain by making a packet copy to verify if the IP checksum is correct.
+ * Contributions to calculating IP checksums from a chained message block with
+ * odd/non-pulled up sizes are welcome.
+ */
+ size_t cbFullMsg = msgdsize(pMsg);
+ mblk_t *pFullMsg = allocb(cbFullMsg, BPRI_MED);
+ Log((DEVICE_NAME ":msgdsize returns %d\n", cbFullMsg));
+ if (RT_UNLIKELY(!pFullMsg))
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisFixChecksums failed to alloc new message of %d bytes.\n", cbFullMsg));
+ return NULL;
+ }
+
+ for (mblk_t *pTmp = pMsg; pTmp; pTmp = pTmp->b_cont)
+ {
+ if (DB_TYPE(pTmp) == M_DATA)
+ {
+ bcopy(pTmp->b_rptr, pFullMsg->b_wptr, MBLKL(pTmp));
+ pFullMsg->b_wptr += MBLKL(pTmp);
+ }
+ }
+
+ DB_TYPE(pFullMsg) = M_DATA;
+ pEthHdr = (PRTNETETHERHDR)pFullMsg->b_rptr;
+ cbIpPacket = MBLKL(pFullMsg) - sizeof(RTNETETHERHDR);
+ }
+ else
+ cbIpPacket = MBLKL(pMsg) - sizeof(RTNETETHERHDR);
+
+ /*
+ * Check if the IP checksum is valid.
+ */
+ uint8_t *pbProtocol = (uint8_t *)(pEthHdr + 1);
+ PRTNETIPV4 pIpHdr = (PRTNETIPV4)pbProtocol;
+ size_t cbPayload = cbIpPacket - (pIpHdr->ip_hl << 2);
+ bool fChecksumAdjusted = false;
+ if (RTNetIPv4IsHdrValid(pIpHdr, cbPayload, cbPayload))
+ {
+ pbProtocol += (pIpHdr->ip_hl << 2);
+
+ /*
+ * Fix up TCP/UDP and IP checksums if they're incomplete/invalid.
+ */
+ if (pIpHdr->ip_p == RTNETIPV4_PROT_TCP)
+ {
+ PRTNETTCP pTcpHdr = (PRTNETTCP)pbProtocol;
+ uint16_t TcpChecksum = RTNetIPv4TCPChecksum(pIpHdr, pTcpHdr, NULL);
+ if (pTcpHdr->th_sum != TcpChecksum)
+ {
+ pTcpHdr->th_sum = TcpChecksum;
+ fChecksumAdjusted = true;
+ Log((DEVICE_NAME ":fixed TCP checksum.\n"));
+ }
+ }
+ else if (pIpHdr->ip_p == RTNETIPV4_PROT_UDP)
+ {
+ PRTNETUDP pUdpHdr = (PRTNETUDP)pbProtocol;
+ uint16_t UdpChecksum = RTNetIPv4UDPChecksum(pIpHdr, pUdpHdr, pUdpHdr + 1);
+
+ if (pUdpHdr->uh_sum != UdpChecksum)
+ {
+ pUdpHdr->uh_sum = UdpChecksum;
+ fChecksumAdjusted = true;
+ Log((DEVICE_NAME ":Fixed UDP checksum."));
+ }
+ }
+ }
+
+ if (fChecksumAdjusted)
+ {
+ /*
+ * If we made a copy and the checksum is corrected on the copy,
+ * free the original, return the checksum fixed copy.
+ */
+ if (pFullMsg)
+ {
+ freemsg(pMsg);
+ return pFullMsg;
+ }
+
+ return pMsg;
+ }
+
+ /*
+ * If we made a copy and the checksum is NOT corrected, free the copy,
+ * and return NULL.
+ */
+ if (pFullMsg)
+ freemsg(pFullMsg);
+
+ return NULL;
+ }
+
+ return NULL;
+}
+
+
+/**
+ * Simple packet dump, used for internal debugging.
+ *
+ * @param pMsg Pointer to the message to analyze and dump.
+ */
+static void vboxNetFltSolarisAnalyzeMBlk(mblk_t *pMsg)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarisAnalyzeMBlk pMsg=%p\n", pMsg));
+
+ PCRTNETETHERHDR pEthHdr = (PCRTNETETHERHDR)pMsg->b_rptr;
+ uint8_t *pb = pMsg->b_rptr;
+ if (pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_IPV4))
+ {
+ PRTNETIPV4 pIpHdr = (PRTNETIPV4)(pEthHdr + 1);
+ size_t cbLen = MBLKL(pMsg) - sizeof(*pEthHdr);
+ if (!pMsg->b_cont)
+ {
+ if (pIpHdr->ip_p == RTNETIPV4_PROT_ICMP)
+ LogRel((DEVICE_NAME ":ICMP D=%.6Rhxs S=%.6Rhxs T=%04x\n", pb, pb + 6, RT_BE2H_U16(*(uint16_t *)(pb + 12))));
+ else if (pIpHdr->ip_p == RTNETIPV4_PROT_TCP)
+ LogRel((DEVICE_NAME ":TCP D=%.6Rhxs S=%.6Rhxs\n", pb, pb + 6));
+ else if (pIpHdr->ip_p == RTNETIPV4_PROT_UDP)
+ {
+ PCRTNETUDP pUdpHdr = (PCRTNETUDP)((uint32_t *)pIpHdr + pIpHdr->ip_hl);
+ if ( RT_BE2H_U16(pUdpHdr->uh_sport) == 67
+ && RT_BE2H_U16(pUdpHdr->uh_dport) == 68)
+ {
+ LogRel((DEVICE_NAME ":UDP bootp ack D=%.6Rhxs S=%.6Rhxs UDP_CheckSum=%04x Computex=%04x\n", pb, pb + 6,
+ RT_BE2H_U16(pUdpHdr->uh_sum), RT_BE2H_U16(RTNetIPv4UDPChecksum(pIpHdr, pUdpHdr, pUdpHdr + 1))));
+ }
+ }
+ }
+ else
+ {
+ Log((DEVICE_NAME ":Chained IP packet. Skipping validity check.\n"));
+ }
+ }
+ else if (pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_VLAN))
+ {
+ PVLANHEADER pVlanHdr = (PVLANHEADER)(pMsg->b_rptr + sizeof(RTNETETHERHDR) - sizeof(pEthHdr->EtherType));
+ LogRel((DEVICE_NAME ":VLAN Pcp=%u Cfi=%u Id=%u\n", VLAN_PRI(RT_BE2H_U16(pVlanHdr->Data)),
+ VLAN_CFI(RT_BE2H_U16(pVlanHdr->Data)), VLAN_ID(RT_BE2H_U16(pVlanHdr->Data))));
+ LogRel((DEVICE_NAME "%.*Rhxd\n", sizeof(VLANHEADER), pVlanHdr));
+ }
+ else if (pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_ARP))
+ {
+ PRTNETARPHDR pArpHdr = (PRTNETARPHDR)(pEthHdr + 1);
+ LogRel((DEVICE_NAME ":ARP Op=%d\n", pArpHdr->ar_oper));
+ }
+ else if (pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_IPV6))
+ {
+ LogRel((DEVICE_NAME ":IPv6 D=%.6Rhxs S=%.6Rhxs\n", pb, pb + 6));
+ }
+ else if (pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_IPX_1)
+ || pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_IPX_2)
+ || pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_IPX_3))
+ {
+ LogRel((DEVICE_NAME ":IPX packet.\n"));
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":Unknown EtherType=%x D=%.6Rhxs S=%.6Rhxs\n", RT_H2BE_U16(pEthHdr->EtherType), &pEthHdr->DstMac,
+ &pEthHdr->SrcMac));
+ /* Log((DEVICE_NAME ":%.*Rhxd\n", MBLKL(pMsg), pMsg->b_rptr)); */
+ }
+}
+#endif
+
+
+/* -=-=-=-=-=- Common Hooks -=-=-=-=-=- */
+
+
+
+void vboxNetFltPortOsSetActive(PVBOXNETFLTINS pThis, bool fActive)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltPortOsSetActive pThis=%p fActive=%d\n", pThis, fActive));
+
+ /*
+ * Enable/disable promiscuous mode.
+ */
+ vboxnetflt_promisc_params_t *pData = RTMemAllocZ(sizeof(vboxnetflt_promisc_params_t));
+ if (RT_LIKELY(pData))
+ {
+ /*
+ * See @bugref{5262} as to why we need to do all this qtimeout/qwriter tricks.
+ */
+ vboxnetflt_promisc_stream_t *pPromiscStream = ASMAtomicUoReadPtrT(&pThis->u.s.pPromiscStream,
+ vboxnetflt_promisc_stream_t *);
+ if ( pPromiscStream
+ && pPromiscStream->Stream.pReadQueue)
+ {
+ pData->pThis = pThis;
+ pData->fPromiscOn = fActive;
+ if (ASMAtomicReadPtr(&pPromiscStream->TimeoutId))
+ quntimeout(WR(pPromiscStream->Stream.pReadQueue), pPromiscStream->TimeoutId);
+ timeout_id_t TimeoutId = qtimeout(WR(pPromiscStream->Stream.pReadQueue), vboxNetFltSolarisPromiscReqWrap,
+ pData, 1 /* ticks */);
+ ASMAtomicWritePtr(&pPromiscStream->TimeoutId, TimeoutId);
+ return; /* pData will be freed by vboxNetFltSolarisPromiscReqWrap() */
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltPortOsSetActive pThis=%p fActive=%d missing stream!\n", pThis, fActive));
+ RTMemFree(pData);
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltPortOsSetActive out of memory!\n"));
+}
+
+
+int vboxNetFltOsDisconnectIt(PVBOXNETFLTINS pThis)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltOsDisconnectIt pThis=%p\n", pThis));
+
+ vboxNetFltSolarisDetachFromInterface(pThis);
+
+ return VINF_SUCCESS;
+}
+
+
+int vboxNetFltOsConnectIt(PVBOXNETFLTINS pThis)
+{
+ /* Nothing to do here. */
+ return VINF_SUCCESS;
+}
+
+
+void vboxNetFltOsDeleteInstance(PVBOXNETFLTINS pThis)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltOsDeleteInstance pThis=%p\n", pThis));
+
+ mutex_destroy(&pThis->u.s.hMtx);
+
+#ifdef VBOXNETFLT_SOLARIS_IPV6_POLLING
+ if (pThis->u.s.hPollMtx != NIL_RTSEMFASTMUTEX)
+ {
+ RTSemFastMutexDestroy(pThis->u.s.hPollMtx);
+ pThis->u.s.hPollMtx = NIL_RTSEMFASTMUTEX;
+ }
+#endif
+
+}
+
+
+int vboxNetFltOsInitInstance(PVBOXNETFLTINS pThis, void *pvContext)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltOsInitInstance pThis=%p\n"));
+
+ /*
+ * Mutex used for loopback lockouts.
+ */
+ int rc = VINF_SUCCESS;
+ mutex_init(&pThis->u.s.hMtx, NULL /* name */, MUTEX_DRIVER, NULL /* cookie */);
+#ifdef VBOXNETFLT_SOLARIS_IPV6_POLLING
+ rc = RTSemFastMutexCreate(&pThis->u.s.hPollMtx);
+ if (RT_SUCCESS(rc))
+ {
+#endif
+ rc = vboxNetFltSolarisAttachToInterface(pThis);
+ if (RT_SUCCESS(rc))
+ return rc;
+
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachToInterface failed. rc=%Rrc\n", rc));
+
+#ifdef VBOXNETFLT_SOLARIS_IPV6_POLLING
+ RTSemFastMutexDestroy(pThis->u.s.hPollMtx);
+ pThis->u.s.hPollMtx = NIL_RTSEMFASTMUTEX;
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltOsInitInstance failed to create poll mutex. rc=%Rrc\n", rc));
+#endif
+
+ mutex_destroy(&pThis->u.s.hMtx);
+
+ NOREF(pvContext);
+ return rc;
+}
+
+
+int vboxNetFltOsPreInitInstance(PVBOXNETFLTINS pThis)
+{
+ /*
+ * Init. the solaris specific data.
+ */
+ pThis->u.s.hIface = NULL;
+ pThis->u.s.pIp4Stream = NULL;
+ pThis->u.s.pIp6Stream = NULL;
+ pThis->u.s.pArpStream = NULL;
+ pThis->u.s.pPromiscStream = NULL;
+ pThis->u.s.fAttaching = false;
+ pThis->u.s.fVLAN = false;
+#ifdef VBOXNETFLT_SOLARIS_IPV6_POLLING
+ pThis->u.s.hPollMtx = NIL_RTSEMFASTMUTEX;
+#endif
+ bzero(&pThis->u.s.MacAddr, sizeof(pThis->u.s.MacAddr));
+ return VINF_SUCCESS;
+}
+
+
+bool vboxNetFltOsMaybeRediscovered(PVBOXNETFLTINS pThis)
+{
+ /*
+ * We don't support interface rediscovery on Solaris hosts because the
+ * filter is very tightly bound to the stream.
+ */
+ return false;
+}
+
+
+void vboxNetFltPortOsNotifyMacAddress(PVBOXNETFLTINS pThis, void *pvIfData, PCRTMAC pMac)
+{
+ NOREF(pThis); NOREF(pvIfData); NOREF(pMac);
+}
+
+
+int vboxNetFltPortOsConnectInterface(PVBOXNETFLTINS pThis, void *pvIf, void **ppvIfData)
+{
+ /* Nothing to do */
+ NOREF(pThis); NOREF(pvIf); NOREF(ppvIfData);
+ return VINF_SUCCESS;
+}
+
+
+int vboxNetFltPortOsDisconnectInterface(PVBOXNETFLTINS pThis, void *pvIfData)
+{
+ /* Nothing to do */
+ NOREF(pThis); NOREF(pvIfData);
+ return VINF_SUCCESS;
+}
+
+
+int vboxNetFltPortOsXmit(PVBOXNETFLTINS pThis, void *pvIfData, PINTNETSG pSG, uint32_t fDst)
+{
+ NOREF(pvIfData);
+ LogFunc((DEVICE_NAME ":vboxNetFltPortOsXmit pThis=%p pSG=%p fDst=%d\n", pThis, pSG, fDst));
+
+ int rc = VINF_SUCCESS;
+ if (fDst & INTNETTRUNKDIR_WIRE)
+ {
+ vboxnetflt_promisc_stream_t *pPromiscStream = ASMAtomicUoReadPtrT(&pThis->u.s.pPromiscStream,
+ vboxnetflt_promisc_stream_t *);
+ if (RT_LIKELY(pPromiscStream))
+ {
+ mblk_t *pMsg = vboxNetFltSolarisMBlkFromSG(pThis, pSG, fDst);
+ if (RT_LIKELY(pMsg))
+ {
+ Log((DEVICE_NAME ":vboxNetFltPortOsXmit INTNETTRUNKDIR_WIRE\n"));
+
+ vboxNetFltSolarisQueueLoopback(pThis, pPromiscStream, pMsg);
+ putnext(WR(pPromiscStream->Stream.pReadQueue), pMsg);
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltPortOsXmit vboxNetFltSolarisMBlkFromSG failed.\n"));
+ return VERR_NO_MEMORY;
+ }
+ }
+ }
+
+ if (fDst & INTNETTRUNKDIR_HOST)
+ {
+ /*
+ * For unplumbed interfaces we would not be bound to IP or ARP.
+ * We either bind to both or neither; so atomic reading one should be sufficient.
+ */
+ vboxnetflt_stream_t *pIp4Stream = ASMAtomicUoReadPtrT(&pThis->u.s.pIp4Stream, vboxnetflt_stream_t *);
+ if (!pIp4Stream)
+ return rc;
+
+ /*
+ * Create a message block and send it up the host stack (upstream).
+ */
+ mblk_t *pMsg = vboxNetFltSolarisMBlkFromSG(pThis, pSG, fDst);
+ if (RT_LIKELY(pMsg))
+ {
+ PCRTNETETHERHDR pEthHdr = (PCRTNETETHERHDR)pMsg->b_rptr;
+
+ /*
+ * Send message up ARP stream.
+ */
+ if (pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_ARP))
+ {
+ Log((DEVICE_NAME ":vboxNetFltPortOsXmit INTNETTRUNKDIR_HOST ARP\n"));
+
+ vboxnetflt_stream_t *pArpStream = ASMAtomicUoReadPtrT(&pThis->u.s.pArpStream, vboxnetflt_stream_t *);
+ if (pArpStream)
+ {
+ /*
+ * Construct a DL_UNITDATA_IND style message for ARP as it doesn't understand fast path.
+ */
+ mblk_t *pDlpiMsg;
+ rc = vboxNetFltSolarisRawToUnitData(pMsg, &pDlpiMsg);
+ if (RT_SUCCESS(rc))
+ {
+ pMsg = pDlpiMsg;
+
+ queue_t *pArpReadQueue = pArpStream->pReadQueue;
+ putnext(pArpReadQueue, pMsg);
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisRawToUnitData failed!\n"));
+ freemsg(pMsg);
+ rc = VERR_NO_MEMORY;
+ }
+ }
+ else
+ freemsg(pMsg); /* Should really never happen... */
+ }
+ else
+ {
+ vboxnetflt_stream_t *pIp6Stream = ASMAtomicUoReadPtrT(&pThis->u.s.pIp6Stream, vboxnetflt_stream_t *);
+ if ( pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_IPV6)
+ && pIp6Stream)
+ {
+ /*
+ * Send messages up IPv6 stream.
+ */
+ Log((DEVICE_NAME ":vboxNetFltPortOsXmit INTNETTRUNKDIR_HOST IPv6\n"));
+
+ pMsg->b_rptr += sizeof(RTNETETHERHDR);
+ queue_t *pIp6ReadQueue = pIp6Stream->pReadQueue;
+ putnext(pIp6ReadQueue, pMsg);
+ }
+ else
+ {
+ /*
+ * Send messages up IPv4 stream.
+ */
+ Log((DEVICE_NAME ":vboxNetFltPortOsXmit INTNETTRUNKDIR_HOST IPv4\n"));
+
+ pMsg->b_rptr += sizeof(RTNETETHERHDR);
+ queue_t *pIp4ReadQueue = pIp4Stream->pReadQueue;
+ putnext(pIp4ReadQueue, pMsg);
+ }
+ }
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisMBlkFromSG failed.\n"));
+ rc = VERR_NO_MEMORY;
+ }
+ }
+
+ return rc;
+}
+
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/solaris/VBoxNetFltBow-solaris.c b/src/VBox/HostDrivers/VBoxNetFlt/solaris/VBoxNetFltBow-solaris.c
new file mode 100644
index 00000000..4a717253
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/solaris/VBoxNetFltBow-solaris.c
@@ -0,0 +1,1702 @@
+/* $Id: VBoxNetFltBow-solaris.c $ */
+/** @file
+ * VBoxNetFlt - Network Filter Driver (Host), Solaris Specific Code.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_NET_FLT_DRV
+#include <VBox/log.h>
+#include <VBox/err.h>
+#include <VBox/intnetinline.h>
+#include <VBox/version.h>
+#include <iprt/initterm.h>
+#include <iprt/alloca.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/string.h>
+#include <iprt/rand.h>
+#include <iprt/net.h>
+#include <iprt/spinlock.h>
+#include <iprt/mem.h>
+
+#include <sys/types.h>
+#include <sys/modctl.h>
+#include <sys/conf.h>
+#include <sys/stat.h>
+#include <sys/ddi.h>
+#include <sys/gld.h>
+#include <sys/sunddi.h>
+#include <sys/strsubr.h>
+#include <sys/dlpi.h>
+#include <sys/dls_mgmt.h>
+#include <sys/mac.h>
+#include <sys/strsun.h>
+
+#include <sys/vnic_mgmt.h>
+#include <sys/mac_client.h>
+#include <sys/mac_provider.h>
+#include <sys/dls.h>
+#include <sys/dld.h>
+#include <sys/cred.h>
+
+
+#define VBOXNETFLT_OS_SPECFIC 1
+#include "../VBoxNetFltInternal.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** The module name. */
+#define DEVICE_NAME "vboxbow"
+/** The module descriptions as seen in 'modinfo'. */
+#define DEVICE_DESC_DRV "VirtualBox NetBow"
+/** The dynamically created VNIC name (hardcoded in NetIf-solaris.cpp).
+ * @todo move this define into a common header. */
+#define VBOXBOW_VNIC_NAME "vboxvnic"
+/** The VirtualBox VNIC template name (hardcoded in NetIf-solaris.cpp).
+ * @todo move this define into a common header. */
+#define VBOXBOW_VNIC_TEMPLATE_NAME "vboxvnic_template"
+/** Debugging switch for using symbols in kmdb */
+# define LOCAL static
+/** VBOXNETFLTVNIC::u32Magic */
+# define VBOXNETFLTVNIC_MAGIC 0x0ddfaced
+
+/** VLAN tag masking, should probably be in IPRT? */
+#define VLAN_ID(vlan) (((vlan) >> 0) & 0x0fffu)
+#define VLAN_CFI(vlan) (((vlan) >> 12) & 0x0001u)
+#define VLAN_PRI(vlan) (((vlan) >> 13) & 0x0007u)
+#define VLAN_TAG(pri,cfi,vid) (((pri) << 13) | ((cfi) << 12) | ((vid) << 0))
+
+typedef struct VLANHEADER
+{
+ uint16_t Type;
+ uint16_t Data;
+} VLANHEADER;
+typedef struct VLANHEADER *PVLANHEADER;
+
+/* Private: from sys/vlan.h */
+#ifndef VLAN_ID_NONE
+# define VLAN_ID_NONE 0
+#endif
+
+/* Private: from sys/param.h */
+#ifndef MAXLINKNAMESPECIFIER
+# define MAXLINKNAMESPECIFIER 96 /* MAXLINKNAMELEN + ZONENAME_MAX */
+#endif
+
+/* Private: from sys/mac_client_priv.h, mac client function prototypes. */
+extern uint16_t mac_client_vid(mac_client_handle_t hClient);
+extern void mac_client_get_resources(mac_client_handle_t hClient, mac_resource_props_t *pResources);
+extern int mac_client_set_resources(mac_client_handle_t hClient, mac_resource_props_t *pResources);
+
+
+/*********************************************************************************************************************************
+* Kernel Entry Hooks *
+*********************************************************************************************************************************/
+LOCAL int VBoxNetFltSolarisAttach(dev_info_t *pDip, ddi_attach_cmd_t enmCmd);
+LOCAL int VBoxNetFltSolarisDetach(dev_info_t *pDip, ddi_detach_cmd_t enmCmd);
+LOCAL int VBoxNetFltSolarisGetInfo(dev_info_t *pDip, ddi_info_cmd_t enmCmd, void *pArg, void **ppvResult);
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * cb_ops: for drivers that support char/block entry points
+ */
+static struct cb_ops g_VBoxNetFltSolarisCbOps =
+{
+ nulldev, /* c open */
+ nulldev, /* c close */
+ nodev, /* b strategy */
+ nodev, /* b dump */
+ nodev, /* b print */
+ nodev, /* c read */
+ nodev, /* c write*/
+ nodev, /* c ioctl*/
+ nodev, /* c devmap */
+ nodev, /* c mmap */
+ nodev, /* c segmap */
+ nochpoll, /* c poll */
+ ddi_prop_op, /* property ops */
+ NULL, /* streamtab */
+ D_NEW | D_MP, /* compat. flag */
+ CB_REV, /* revision */
+ nodev, /* c aread */
+ nodev /* c awrite */
+};
+
+/**
+ * dev_ops: for driver device operations
+ */
+static struct dev_ops g_VBoxNetFltSolarisDevOps =
+{
+ DEVO_REV, /* driver build revision */
+ 0, /* ref count */
+ VBoxNetFltSolarisGetInfo,
+ nulldev, /* identify */
+ nulldev, /* probe */
+ VBoxNetFltSolarisAttach,
+ VBoxNetFltSolarisDetach,
+ nodev, /* reset */
+ &g_VBoxNetFltSolarisCbOps,
+ NULL, /* bus ops */
+ nodev, /* power */
+ ddi_quiesce_not_needed
+};
+
+/**
+ * modldrv: export driver specifics to the kernel
+ */
+static struct modldrv g_VBoxNetFltSolarisModule =
+{
+ &mod_driverops, /* extern from kernel */
+ DEVICE_DESC_DRV " " VBOX_VERSION_STRING "r" RT_XSTR(VBOX_SVN_REV),
+ &g_VBoxNetFltSolarisDevOps
+};
+
+/**
+ * modlinkage: export install/remove/info to the kernel
+ */
+static struct modlinkage g_VBoxNetFltSolarisModLinkage =
+{
+ MODREV_1,
+ {
+ &g_VBoxNetFltSolarisModule,
+ NULL,
+ }
+};
+
+/*
+ * VBOXNETFLTVNICTEMPLATE: VNIC template information.
+ */
+typedef struct VBOXNETFLTVNICTEMPLATE
+{
+ /** The name of link on which the VNIC template is created on. */
+ char szLinkName[MAXNAMELEN];
+ /** The VLAN Id (can be VLAN_ID_NONE). */
+ uint16_t uVLANId;
+ /** Resources (bandwidth, CPU bindings, flow priority etc.) */
+ mac_resource_props_t Resources;
+} VBOXNETFLTVNICTEMPLATE;
+typedef struct VBOXNETFLTVNICTEMPLATE *PVBOXNETFLTVNICTEMPLATE;
+
+/**
+ * VBOXNETFLTVNIC: Per-VNIC instance data.
+ */
+typedef struct VBOXNETFLTVNIC
+{
+ /** Magic number (VBOXNETFLTVNIC_MAGIC). */
+ uint32_t u32Magic;
+ /** Whether we created the VNIC or not. */
+ bool fCreated;
+ /** Pointer to the VNIC template if any. */
+ PVBOXNETFLTVNICTEMPLATE pVNICTemplate;
+ /** Pointer to the VirtualBox interface instance. */
+ void *pvIf;
+ /** The MAC handle. */
+ mac_handle_t hInterface;
+ /** The VNIC link ID. */
+ datalink_id_t hLinkId;
+ /** The MAC client handle */
+ mac_client_handle_t hClient;
+ /** The unicast address handle. */
+ mac_unicast_handle_t hUnicast;
+ /** The promiscuous handle. */
+ mac_promisc_handle_t hPromisc;
+ /* The VNIC name. */
+ char szName[MAXLINKNAMESPECIFIER];
+ /** Handle to the next VNIC in the list. */
+ list_node_t hNode;
+} VBOXNETFLTVNIC;
+typedef struct VBOXNETFLTVNIC *PVBOXNETFLTVNIC;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Global Device handle we only support one instance. */
+static dev_info_t *g_pVBoxNetFltSolarisDip = NULL;
+/** The (common) global data. */
+static VBOXNETFLTGLOBALS g_VBoxNetFltSolarisGlobals;
+/** Global next-free VNIC Id (never decrements). */
+static volatile uint64_t g_VBoxNetFltSolarisVNICId;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+LOCAL mblk_t *vboxNetFltSolarisMBlkFromSG(PVBOXNETFLTINS pThis, PINTNETSG pSG, uint32_t fDst);
+LOCAL unsigned vboxNetFltSolarisMBlkCalcSGSegs(PVBOXNETFLTINS pThis, mblk_t *pMsg);
+LOCAL int vboxNetFltSolarisMBlkToSG(PVBOXNETFLTINS pThis, mblk_t *pMsg, PINTNETSG pSG, unsigned cSegs, uint32_t fSrc);
+LOCAL void vboxNetFltSolarisRecv(void *pvData, mac_resource_handle_t hResource, mblk_t *pMsg, boolean_t fLoopback);
+//LOCAL void vboxNetFltSolarisAnalyzeMBlk(mblk_t *pMsg);
+LOCAL int vboxNetFltSolarisReportInfo(PVBOXNETFLTINS pThis, mac_handle_t hInterface, bool fIsVNIC);
+LOCAL int vboxNetFltSolarisInitVNIC(PVBOXNETFLTINS pThis, PVBOXNETFLTVNIC pVNIC);
+LOCAL int vboxNetFltSolarisInitVNICTemplate(PVBOXNETFLTINS pThis, PVBOXNETFLTVNICTEMPLATE pVNICTemplate);
+LOCAL PVBOXNETFLTVNIC vboxNetFltSolarisAllocVNIC(void);
+LOCAL void vboxNetFltSolarisFreeVNIC(PVBOXNETFLTVNIC pVNIC);
+LOCAL void vboxNetFltSolarisDestroyVNIC(PVBOXNETFLTVNIC pVNIC);
+LOCAL int vboxNetFltSolarisCreateVNIC(PVBOXNETFLTINS pThis, PVBOXNETFLTVNIC *ppVNIC);
+DECLINLINE(int) vboxNetFltSolarisGetLinkId(const char *pszMacName, datalink_id_t *pLinkId);
+
+/**
+ * Kernel entry points
+ */
+int _init(void)
+{
+ Log((DEVICE_NAME ":_init\n"));
+
+ /*
+ * Prevent module autounloading.
+ */
+ modctl_t *pModCtl = mod_getctl(&g_VBoxNetFltSolarisModLinkage);
+ if (pModCtl)
+ pModCtl->mod_loadflags |= MOD_NOAUTOUNLOAD;
+ else
+ cmn_err(CE_NOTE, ":failed to disable autounloading!\n");
+
+ /*
+ * Initialize IPRT.
+ */
+ int rc = RTR0Init(0);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Initialize the globals and connect to the support driver.
+ *
+ * This will call back vboxNetFltOsOpenSupDrv (and maybe vboxNetFltOsCloseSupDrv)
+ * for establishing the connect to the support driver.
+ */
+ memset(&g_VBoxNetFltSolarisGlobals, 0, sizeof(g_VBoxNetFltSolarisGlobals));
+ rc = vboxNetFltInitGlobalsAndIdc(&g_VBoxNetFltSolarisGlobals);
+ if (RT_SUCCESS(rc))
+ {
+ rc = mod_install(&g_VBoxNetFltSolarisModLinkage);
+ if (!rc)
+ return rc;
+
+ LogRel((DEVICE_NAME ":mod_install failed. rc=%d\n", rc));
+ vboxNetFltTryDeleteIdcAndGlobals(&g_VBoxNetFltSolarisGlobals);
+ }
+ else
+ LogRel((DEVICE_NAME ":failed to initialize globals.\n"));
+
+ RTR0Term();
+ }
+ else
+ cmn_err(CE_NOTE, "failed to initialize IPRT (rc=%d)\n", rc);
+
+ memset(&g_VBoxNetFltSolarisGlobals, 0, sizeof(g_VBoxNetFltSolarisGlobals));
+ return RTErrConvertToErrno(rc);
+}
+
+
+int _fini(void)
+{
+ int rc;
+ Log((DEVICE_NAME ":_fini\n"));
+
+ /*
+ * Undo the work done during start (in reverse order).
+ */
+ rc = vboxNetFltTryDeleteIdcAndGlobals(&g_VBoxNetFltSolarisGlobals);
+ if (RT_FAILURE(rc))
+ {
+ LogRel((DEVICE_NAME ":_fini - busy! rc=%d\n", rc));
+ return EBUSY;
+ }
+
+ rc = mod_remove(&g_VBoxNetFltSolarisModLinkage);
+ if (!rc)
+ RTR0Term();
+
+ return rc;
+}
+
+
+int _info(struct modinfo *pModInfo)
+{
+ /* _info() can be called before _init() so RTR0Init() might not be called at this point. */
+ int rc = mod_info(&g_VBoxNetFltSolarisModLinkage, pModInfo);
+ 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.
+ */
+LOCAL int VBoxNetFltSolarisAttach(dev_info_t *pDip, ddi_attach_cmd_t enmCmd)
+{
+ Log((DEVICE_NAME ":VBoxNetFltSolarisAttach pDip=%p enmCmd=%d\n", pDip, enmCmd));
+
+ switch (enmCmd)
+ {
+ case DDI_ATTACH:
+ {
+ g_pVBoxNetFltSolarisDip = pDip;
+ return DDI_SUCCESS;
+ }
+
+ 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.
+ */
+LOCAL int VBoxNetFltSolarisDetach(dev_info_t *pDip, ddi_detach_cmd_t enmCmd)
+{
+ Log((DEVICE_NAME ":VBoxNetFltSolarisDetach pDip=%p enmCmd=%d\n", pDip, enmCmd));
+
+ switch (enmCmd)
+ {
+ case DDI_DETACH:
+ {
+ return DDI_SUCCESS;
+ }
+
+ case DDI_RESUME:
+ {
+ /* Nothing to do here... */
+ return DDI_SUCCESS;
+ }
+
+ /* case DDI_PM_SUSPEND: */
+ /* case DDI_HOT_PLUG_DETACH: */
+ default:
+ return DDI_FAILURE;
+ }
+}
+
+
+/**
+ * Info entry point, called by solaris kernel for obtaining driver info.
+ *
+ * @param pDip The module structure instance (do not use).
+ * @param enmCmd Information request type.
+ * @param pvArg Type specific argument.
+ * @param ppvResult Where to store the requested info.
+ *
+ * @returns corresponding solaris error code.
+ */
+LOCAL int VBoxNetFltSolarisGetInfo(dev_info_t *pDip, ddi_info_cmd_t enmCmd, void *pvArg, void **ppvResult)
+{
+ Log((DEVICE_NAME ":VBoxNetFltSolarisGetInfo pDip=%p enmCmd=%d pArg=%p instance=%d\n", pDip, enmCmd, getminor((dev_t)pvArg)));
+
+ switch (enmCmd)
+ {
+ case DDI_INFO_DEVT2DEVINFO:
+ {
+ *ppvResult = g_pVBoxNetFltSolarisDip;
+ return *ppvResult ? DDI_SUCCESS : DDI_FAILURE;
+ }
+
+ case DDI_INFO_DEVT2INSTANCE:
+ {
+ /* There can only be a single-instance of this driver and thus its instance number is 0. */
+ *ppvResult = (void *)0;
+ break;
+ }
+ }
+
+ return DDI_FAILURE;
+}
+
+
+/**
+ * Create a solaris message block from the SG list.
+ *
+ * @param pThis The instance.
+ * @param pSG Pointer to the scatter-gather list.
+ * @param fDst INTNETTRUNKDIR_XXX.
+ *
+ * @returns Solaris message block.
+ */
+DECLINLINE(mblk_t *) vboxNetFltSolarisMBlkFromSG(PVBOXNETFLTINS pThis, PINTNETSG pSG, uint32_t fDst)
+{
+ Log((DEVICE_NAME ":vboxNetFltSolarisMBlkFromSG pThis=%p pSG=%p\n", pThis, pSG));
+
+ mblk_t *pMsg = allocb(pSG->cbTotal, BPRI_HI);
+ if (RT_UNLIKELY(!pMsg))
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisMBlkFromSG failed to alloc %d bytes for mblk_t.\n", pSG->cbTotal));
+ return NULL;
+ }
+
+ /*
+ * Single buffer copy. Maybe later explore the
+ * need/possibility for using a mblk_t chain rather.
+ */
+ for (unsigned i = 0; i < pSG->cSegsUsed; i++)
+ {
+ if (pSG->aSegs[i].pv)
+ {
+ bcopy(pSG->aSegs[i].pv, pMsg->b_wptr, pSG->aSegs[i].cb);
+ pMsg->b_wptr += pSG->aSegs[i].cb;
+ }
+ }
+ return pMsg;
+}
+
+
+/**
+ * Calculate the number of segments required for this message block.
+ *
+ * @param pThis The instance
+ * @param pMsg Pointer to the data message.
+ *
+ * @returns Number of segments.
+ */
+LOCAL unsigned vboxNetFltSolarisMBlkCalcSGSegs(PVBOXNETFLTINS pThis, mblk_t *pMsg)
+{
+ unsigned cSegs = 0;
+ for (mblk_t *pCur = pMsg; pCur; pCur = pCur->b_cont)
+ if (MBLKL(pCur))
+ cSegs++;
+
+#ifdef PADD_RUNT_FRAMES_FROM_HOST
+ if (msgdsize(pMsg) < 60)
+ cSegs++;
+#endif
+
+ NOREF(pThis);
+ return RT_MAX(cSegs, 1);
+}
+
+
+/**
+ * Initializes an SG list from the given message block.
+ *
+ * @param pThis The instance.
+ * @param pMsg Pointer to the data message.
+ The caller must ensure it's not a control message block.
+ * @param pSG Pointer to the SG.
+ * @param cSegs Number of segments in the SG.
+ * This should match the number in the message block exactly!
+ * @param fSrc The source of the message.
+ *
+ * @returns VBox status code.
+ */
+LOCAL int vboxNetFltSolarisMBlkToSG(PVBOXNETFLTINS pThis, mblk_t *pMsg, PINTNETSG pSG, unsigned cSegs, uint32_t fSrc)
+{
+ Log((DEVICE_NAME ":vboxNetFltSolarisMBlkToSG pThis=%p pMsg=%p pSG=%p cSegs=%d\n", pThis, pMsg, pSG, cSegs));
+
+ /*
+ * Convert the message block to segments. Works cbTotal and sets cSegsUsed.
+ */
+ IntNetSgInitTempSegs(pSG, 0 /*cbTotal*/, cSegs, 0 /*cSegsUsed*/);
+ mblk_t *pCur = pMsg;
+ unsigned iSeg = 0;
+ while (pCur)
+ {
+ size_t cbSeg = MBLKL(pCur);
+ if (cbSeg)
+ {
+ void *pvSeg = pCur->b_rptr;
+ pSG->aSegs[iSeg].pv = pvSeg;
+ pSG->aSegs[iSeg].cb = cbSeg;
+ pSG->aSegs[iSeg].Phys = NIL_RTHCPHYS;
+ pSG->cbTotal += cbSeg;
+ iSeg++;
+ }
+ pCur = pCur->b_cont;
+ }
+ pSG->cSegsUsed = iSeg;
+
+#ifdef PADD_RUNT_FRAMES_FROM_HOST
+ if (pSG->cbTotal < 60 && (fSrc & INTNETTRUNKDIR_HOST))
+ {
+ Log((DEVICE_NAME ":vboxNetFltSolarisMBlkToSG pulling up to length.\n"));
+
+ static uint8_t const s_abZero[128] = {0};
+ pSG->aSegs[iSeg].Phys = NIL_RTHCPHYS;
+ pSG->aSegs[iSeg].pv = (void *)&s_abZero[0];
+ pSG->aSegs[iSeg].cb = 60 - pSG->cbTotal;
+ pSG->cbTotal = 60;
+ pSG->cSegsUsed++;
+ Assert(iSeg + 1 < cSegs);
+ }
+#endif
+
+ Log((DEVICE_NAME ":vboxNetFltSolarisMBlkToSG iSeg=%d pSG->cbTotal=%d msgdsize=%d\n", iSeg, pSG->cbTotal, msgdsize(pMsg)));
+ return VINF_SUCCESS;
+}
+
+
+#if 0
+/**
+ * Simple packet dump, used for internal debugging.
+ *
+ * @param pMsg Pointer to the message to analyze and dump.
+ */
+LOCAL void vboxNetFltSolarisAnalyzeMBlk(mblk_t *pMsg)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarisAnalyzeMBlk pMsg=%p\n", pMsg));
+
+ PCRTNETETHERHDR pEthHdr = (PCRTNETETHERHDR)pMsg->b_rptr;
+ uint8_t *pb = pMsg->b_rptr;
+ if (pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_IPV4))
+ {
+ PRTNETIPV4 pIpHdr = (PRTNETIPV4)(pEthHdr + 1);
+ if (!pMsg->b_cont)
+ {
+ if (pIpHdr->ip_p == RTNETIPV4_PROT_ICMP)
+ LogRel((DEVICE_NAME ":ICMP D=%.6Rhxs S=%.6Rhxs T=%04x\n", pb, pb + 6, RT_BE2H_U16(*(uint16_t *)(pb + 12))));
+ else if (pIpHdr->ip_p == RTNETIPV4_PROT_TCP)
+ LogRel((DEVICE_NAME ":TCP D=%.6Rhxs S=%.6Rhxs\n", pb, pb + 6));
+ else if (pIpHdr->ip_p == RTNETIPV4_PROT_UDP)
+ {
+ PCRTNETUDP pUdpHdr = (PCRTNETUDP)((uint32_t *)pIpHdr + pIpHdr->ip_hl);
+ if ( RT_BE2H_U16(pUdpHdr->uh_sport) == 67
+ && RT_BE2H_U16(pUdpHdr->uh_dport) == 68)
+ {
+ LogRel((DEVICE_NAME ":UDP bootp ack D=%.6Rhxs S=%.6Rhxs UDP_CheckSum=%04x Computex=%04x\n", pb, pb + 6,
+ RT_BE2H_U16(pUdpHdr->uh_sum), RT_BE2H_U16(RTNetIPv4UDPChecksum(pIpHdr, pUdpHdr, pUdpHdr + 1))));
+ }
+ }
+ }
+ else
+ {
+ Log((DEVICE_NAME ":Chained IP packet. Skipping validity check.\n"));
+ }
+ }
+ else if (pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_VLAN))
+ {
+ PVLANHEADER pVlanHdr = (PVLANHEADER)(pMsg->b_rptr + sizeof(RTNETETHERHDR) - sizeof(pEthHdr->EtherType));
+ LogRel((DEVICE_NAME ":VLAN Pcp=%u Cfi=%u Id=%u\n", VLAN_PRI(RT_BE2H_U16(pVlanHdr->Data)),
+ VLAN_CFI(RT_BE2H_U16(pVlanHdr->Data)), VLAN_ID(RT_BE2H_U16(pVlanHdr->Data))));
+ LogRel((DEVICE_NAME "%.*Rhxd\n", sizeof(VLANHEADER), pVlanHdr));
+ }
+ else if (pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_ARP))
+ {
+ PRTNETARPHDR pArpHdr = (PRTNETARPHDR)(pEthHdr + 1);
+ LogRel((DEVICE_NAME ":ARP Op=%d\n", pArpHdr->ar_oper));
+ }
+ else if (pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_IPV6))
+ {
+ LogRel((DEVICE_NAME ":IPv6 D=%.6Rhxs S=%.6Rhxs\n", pb, pb + 6));
+ }
+ else if ( pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_IPX_1)
+ || pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_IPX_2)
+ || pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_IPX_3))
+ {
+ LogRel((DEVICE_NAME ":IPX packet.\n"));
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":Unknown EtherType=%x D=%.6Rhxs S=%.6Rhxs\n", RT_H2BE_U16(pEthHdr->EtherType), &pEthHdr->DstMac,
+ &pEthHdr->SrcMac));
+ /* Log((DEVICE_NAME ":%.*Rhxd\n", MBLKL(pMsg), pMsg->b_rptr)); */
+ }
+}
+#endif
+
+
+/**
+ * Helper.
+ */
+DECLINLINE(bool) vboxNetFltPortSolarisIsHostMac(PVBOXNETFLTINS pThis, PCRTMAC pMac)
+{
+ return pThis->u.s.MacAddr.au16[0] == pMac->au16[0]
+ && pThis->u.s.MacAddr.au16[1] == pMac->au16[1]
+ && pThis->u.s.MacAddr.au16[2] == pMac->au16[2];
+}
+
+
+/**
+ * Receive (rx) entry point.
+ *
+ * @param pvData Private data.
+ * @param hResource The resource handle.
+ * @param pMsg The packet.
+ * @param fLoopback Whether this is a loopback packet or not.
+ */
+LOCAL void vboxNetFltSolarisRecv(void *pvData, mac_resource_handle_t hResource, mblk_t *pMsg, boolean_t fLoopback)
+{
+ Log((DEVICE_NAME ":vboxNetFltSolarisRecv pvData=%p pMsg=%p fLoopback=%d cbData=%d\n", pvData, pMsg, fLoopback,
+ pMsg ? MBLKL(pMsg) : 0));
+
+ PVBOXNETFLTINS pThis = (PVBOXNETFLTINS)pvData;
+ AssertPtrReturnVoid(pThis);
+ AssertPtrReturnVoid(pMsg);
+
+ /*
+ * Active? Retain the instance and increment the busy counter.
+ */
+ if (!vboxNetFltTryRetainBusyActive(pThis))
+ {
+ freemsgchain(pMsg);
+ return;
+ }
+
+ uint32_t fSrc = INTNETTRUNKDIR_WIRE;
+ PRTNETETHERHDR pEthHdr = (PRTNETETHERHDR)pMsg->b_rptr;
+ if ( MBLKL(pMsg) >= sizeof(RTNETETHERHDR)
+ && vboxNetFltPortSolarisIsHostMac(pThis, &pEthHdr->SrcMac))
+ fSrc = INTNETTRUNKDIR_HOST;
+
+ /*
+ * Route all received packets into the internal network.
+ */
+ uint16_t cFailed = 0;
+ for (mblk_t *pCurMsg = pMsg; pCurMsg != NULL; pCurMsg = pCurMsg->b_next)
+ {
+ unsigned cSegs = vboxNetFltSolarisMBlkCalcSGSegs(pThis, pCurMsg);
+ PINTNETSG pSG = (PINTNETSG)alloca(RT_UOFFSETOF_DYN(INTNETSG, aSegs[cSegs]));
+ int rc = vboxNetFltSolarisMBlkToSG(pThis, pMsg, pSG, cSegs, fSrc);
+ if (RT_SUCCESS(rc))
+ pThis->pSwitchPort->pfnRecv(pThis->pSwitchPort, NULL, pSG, fSrc);
+ else
+ cFailed++;
+ }
+ vboxNetFltRelease(pThis, true /* fBusy */);
+
+ if (RT_UNLIKELY(cFailed))
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisMBlkToSG failed for %u packets.\n", cFailed));
+
+ freemsgchain(pMsg);
+
+ NOREF(hResource);
+}
+
+
+#if 0
+/**
+ * MAC layer link notification hook.
+ *
+ * @param pvArg Opaque pointer to the instance.
+ * @param Type Notification Type.
+ *
+ * @remarks This hook will be invoked for various changes to the underlying
+ * interface even when VMs aren't running so don't do any funky stuff
+ * here.
+ */
+LOCAL void vboxNetFltSolarisLinkNotify(void *pvArg, mac_notify_type_t Type)
+{
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisLinkNotify pvArg=%p Type=%d\n", pvArg, Type));
+
+ PVBOXNETFLTINS pThis = pvArg;
+ AssertPtrReturnVoid(pThis);
+ AssertPtrReturnVoid(pThis->u.s.hInterface);
+
+ switch (Type)
+ {
+ case MAC_NOTE_LINK:
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisLinkNotify link state change\n"));
+ link_state_t hLinkState = mac_stat_get(pThis->u.s.hInterface, MAC_STAT_LINK_STATE);
+ bool fDisconnectedFromHost = hLinkState == LINK_STATE_UP ? false : true;
+ if (fDisconnectedFromHost != ASMAtomicUoReadBool(&pThis->fDisconnectedFromHost))
+ {
+ ASMAtomicUoWriteBool(&pThis->fDisconnectedFromHost, fDisconnectedFromHost);
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisLinkNotify link state change: new state=%s\n",
+ fDisconnectedFromHost ? "DOWN" : "UP"));
+ }
+ break;
+ }
+
+ default:
+ return;
+ }
+}
+#endif
+
+
+/**
+ * Report capabilities and MAC address to IntNet after obtaining the MAC address
+ * of the underlying interface for a VNIC or the current interface if it's a
+ * physical/ether-stub interface.
+ *
+ * @param pThis The instance.
+ * @param hInterface The Interface handle.
+ * @param fIsVNIC Whether this interface handle corresponds to a VNIC
+ * or not.
+ *
+ * @remarks Retains the instance while doing it's job.
+ * @returns VBox status code.
+ */
+LOCAL int vboxNetFltSolarisReportInfo(PVBOXNETFLTINS pThis, mac_handle_t hInterface, bool fIsVNIC)
+{
+ mac_handle_t hLowerMac = NULL;
+ if (!fIsVNIC)
+ hLowerMac = hInterface;
+ else
+ {
+ hLowerMac = mac_get_lower_mac_handle(hInterface);
+ if (RT_UNLIKELY(!hLowerMac))
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisReportInfo failed to get lower MAC handle for '%s'\n", pThis->szName));
+ return VERR_INVALID_HANDLE;
+ }
+ }
+
+ pThis->u.s.hInterface = hLowerMac;
+
+#if 0
+ /*
+ * Try setup link notification hooks, this might fail if mac_no_notification()
+ * doesn't support it. We won't bother using the private function since link notification
+ * isn't critical for us and ignore failures.
+ */
+ pThis->u.s.hNotify = mac_notify_add(hLowerMac, vboxNetFltSolarisLinkNotify, pThis);
+ if (!pThis->u.s.hNotify)
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisReportInfo Warning! Failed to setup link notification hook.\n"));
+#endif
+
+ mac_unicast_primary_get(hLowerMac, (uint8_t *)pThis->u.s.MacAddr.au8);
+ if (vboxNetFltTryRetainBusyNotDisconnected(pThis))
+ {
+ Assert(pThis->pSwitchPort);
+ Log((DEVICE_NAME ":vboxNetFltSolarisReportInfo phys mac %.6Rhxs\n", &pThis->u.s.MacAddr));
+ pThis->pSwitchPort->pfnReportMacAddress(pThis->pSwitchPort, &pThis->u.s.MacAddr);
+ pThis->pSwitchPort->pfnReportPromiscuousMode(pThis->pSwitchPort, false); /** @todo Promisc */
+ pThis->pSwitchPort->pfnReportGsoCapabilities(pThis->pSwitchPort, 0, INTNETTRUNKDIR_WIRE | INTNETTRUNKDIR_HOST);
+ pThis->pSwitchPort->pfnReportNoPreemptDsts(pThis->pSwitchPort, 0 /* none */);
+ vboxNetFltRelease(pThis, true /*fBusy*/);
+ return VINF_SUCCESS;
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisReportInfo failed to retain interface. pThis=%p\n", pThis));
+
+ return VERR_INTNET_FLT_IF_BUSY;
+}
+
+
+/**
+ * Initialize a VNIC, optionally from a template.
+ *
+ * @param pThis The instance.
+ * @param pVNIC Pointer to the VNIC.
+ *
+ * @returns VBox status code.
+ */
+LOCAL int vboxNetFltSolarisInitVNIC(PVBOXNETFLTINS pThis, PVBOXNETFLTVNIC pVNIC)
+{
+ /*
+ * Some paranoia.
+ */
+ AssertReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pVNIC, VERR_INVALID_PARAMETER);
+ AssertReturn(pVNIC->hInterface, VERR_INVALID_POINTER);
+ AssertReturn(pVNIC->hLinkId != DATALINK_INVALID_LINKID, VERR_INVALID_HANDLE);
+ AssertReturn(!pVNIC->hClient, VERR_INVALID_POINTER);
+
+ int rc = mac_client_open(pVNIC->hInterface, &pVNIC->hClient,
+ NULL, /* name of this client */
+ MAC_OPEN_FLAGS_USE_DATALINK_NAME | /* client name same as underlying NIC */
+ MAC_OPEN_FLAGS_MULTI_PRIMARY /* allow multiple primary unicasts */
+ );
+ if (RT_LIKELY(!rc))
+ {
+ if (pVNIC->pVNICTemplate)
+ rc = mac_client_set_resources(pVNIC->hClient, &pVNIC->pVNICTemplate->Resources);
+
+ if (RT_LIKELY(!rc))
+ {
+ Log((DEVICE_NAME ":vboxNetFltSolarisInitVNIC succesfully initialized VNIC.\n"));
+ return VINF_SUCCESS;
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisInitVNIC mac_client_set_resources failed. rc=%d\n", rc));
+ rc = VERR_INTNET_FLT_VNIC_INIT_FAILED;
+ }
+
+ mac_client_close(pVNIC->hClient, 0 /* flags */);
+ pVNIC->hClient = NULL;
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisInitVNIC failed to open mac client for '%s' rc=%d\n", pThis->szName, rc));
+
+ return VERR_INTNET_FLT_VNIC_OPEN_FAILED;
+}
+
+
+
+/**
+ * Get the underlying link name for a VNIC (template).
+ *
+ * @return VBox status code.
+ * @param hVNICMacHandle The handle to the VNIC.
+ * @param pszLowerLinkName Where to store the lower-mac linkname, must be
+ * at least MAXLINKNAMELEN in size.
+ */
+LOCAL int vboxNetFltSolarisGetLowerLinkName(mac_handle_t hVNICMacHandle, char *pszLowerLinkName)
+{
+ Assert(mac_is_vnic(hVNICMacHandle));
+ mac_handle_t hPhysLinkHandle = mac_get_lower_mac_handle(hVNICMacHandle);
+ if (RT_LIKELY(hPhysLinkHandle))
+ {
+ datalink_id_t PhysLinkId;
+ const char *pszMacName = mac_name(hPhysLinkHandle);
+ int rc = vboxNetFltSolarisGetLinkId(pszMacName, &PhysLinkId);
+ if (RT_SUCCESS(rc))
+ {
+ rc = dls_mgmt_get_linkinfo(PhysLinkId, pszLowerLinkName, NULL /*class*/, NULL /*media*/, NULL /*flags*/);
+ if (RT_LIKELY(!rc))
+ return VINF_SUCCESS;
+
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisGetLowerLinkName failed to get link info. pszMacName=%s pszLowerLinkName=%s\n",
+ pszMacName, pszLowerLinkName));
+ return VERR_INTNET_FLT_LOWER_LINK_INFO_NOT_FOUND;
+ }
+
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisGetLowerLinkName failed to get link id. pszMacName=%s pszLowerLinkName=%s\n",
+ pszMacName, pszLowerLinkName));
+ return VERR_INTNET_FLT_LOWER_LINK_ID_NOT_FOUND;
+ }
+
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisGetLowerLinkName failed to get lower-mac. pszLowerLinkName=%s\n", pszLowerLinkName));
+ return VERR_INTNET_FLT_LOWER_LINK_OPEN_FAILED;
+}
+
+
+/**
+ * Initializes the VNIC template. This involves opening the template VNIC to
+ * retreive info. like the VLAN Id, underlying MAC address etc.
+ *
+ * @param pThis The VM connection instance.
+ * @param pVNICTemplate Pointer to a VNIC template to initialize.
+ *
+ * @returns VBox status code.
+ */
+LOCAL int vboxNetFltSolarisInitVNICTemplate(PVBOXNETFLTINS pThis, PVBOXNETFLTVNICTEMPLATE pVNICTemplate)
+{
+ Log((DEVICE_NAME ":vboxNetFltSolarisInitVNICTemplate pThis=%p pVNICTemplate=%p\n", pThis, pVNICTemplate));
+
+ AssertReturn(pVNICTemplate, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u.s.fIsVNICTemplate == true, VERR_INVALID_STATE);
+
+ /*
+ * Get the VNIC template's datalink ID.
+ */
+ datalink_id_t VNICLinkId;
+ int rc = vboxNetFltSolarisGetLinkId(pThis->szName, &VNICLinkId);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Open the VNIC to obtain a MAC handle so as to retreive the VLAN ID.
+ */
+ mac_handle_t hInterface;
+ rc = mac_open_by_linkid(VNICLinkId, &hInterface);
+ if (!rc)
+ {
+ /*
+ * Get the underlying linkname.
+ */
+ AssertCompile(sizeof(pVNICTemplate->szLinkName) >= MAXLINKNAMELEN);
+ rc = vboxNetFltSolarisGetLowerLinkName(hInterface, pVNICTemplate->szLinkName);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Now open the VNIC template to retrieve the VLAN Id & resources.
+ */
+ mac_client_handle_t hClient;
+ rc = mac_client_open(hInterface, &hClient,
+ NULL, /* name of this client */
+ MAC_OPEN_FLAGS_USE_DATALINK_NAME | /* client name same as underlying NIC */
+ MAC_OPEN_FLAGS_MULTI_PRIMARY /* allow multiple primary unicasts */
+ );
+ if (RT_LIKELY(!rc))
+ {
+ pVNICTemplate->uVLANId = mac_client_vid(hClient);
+ mac_client_get_resources(hClient, &pVNICTemplate->Resources);
+ mac_client_close(hClient, 0 /* fFlags */);
+ mac_close(hInterface);
+
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisInitVNICTemplate successfully init. VNIC template. szLinkName=%s "
+ "VLAN Id=%u\n", pVNICTemplate->szLinkName, pVNICTemplate->uVLANId));
+ return VINF_SUCCESS;
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisInitVNICTemplate failed to open VNIC template. rc=%d\n", rc));
+ rc = VERR_INTNET_FLT_IF_FAILED;
+ }
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisInitVNICTemplate failed to get lower linkname for VNIC template '%s'.\n",
+ pThis->szName));
+
+ mac_close(hInterface);
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisInitVNICTemplate failed to open by link ID. rc=%d\n", rc));
+ rc = VERR_INTNET_FLT_IF_FAILED;
+ }
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisInitVNICTemplate failed to get VNIC template link Id. rc=%d\n", rc));
+
+ return rc;
+}
+
+
+/**
+ * Allocate a VNIC structure.
+ *
+ * @returns An allocated VNIC structure or NULL in case of errors.
+ */
+LOCAL PVBOXNETFLTVNIC vboxNetFltSolarisAllocVNIC(void)
+{
+ PVBOXNETFLTVNIC pVNIC = RTMemAllocZ(sizeof(VBOXNETFLTVNIC));
+ if (RT_UNLIKELY(!pVNIC))
+ return NULL;
+
+ pVNIC->u32Magic = VBOXNETFLTVNIC_MAGIC;
+ pVNIC->fCreated = false;
+ pVNIC->pVNICTemplate = NULL;
+ pVNIC->pvIf = NULL;
+ pVNIC->hInterface = NULL;
+ pVNIC->hLinkId = DATALINK_INVALID_LINKID;
+ pVNIC->hClient = NULL;
+ pVNIC->hUnicast = NULL;
+ pVNIC->hPromisc = NULL;
+ RT_ZERO(pVNIC->szName);
+ list_link_init(&pVNIC->hNode);
+ return pVNIC;
+}
+
+
+/**
+ * Frees an allocated VNIC.
+ *
+ * @param pVNIC Pointer to the VNIC.
+ */
+DECLINLINE(void) vboxNetFltSolarisFreeVNIC(PVBOXNETFLTVNIC pVNIC)
+{
+ RTMemFree(pVNIC);
+}
+
+
+/**
+ * Destroy a created VNIC if it was created by us, or just
+ * de-initializes the VNIC freeing up resources handles.
+ *
+ * @param pVNIC Pointer to the VNIC.
+ */
+LOCAL void vboxNetFltSolarisDestroyVNIC(PVBOXNETFLTVNIC pVNIC)
+{
+ AssertPtrReturnVoid(pVNIC);
+ AssertMsgReturnVoid(pVNIC->u32Magic == VBOXNETFLTVNIC_MAGIC, ("pVNIC=%p u32Magic=%#x\n", pVNIC, pVNIC->u32Magic));
+ if (pVNIC)
+ {
+ if (pVNIC->hClient)
+ {
+#if 0
+ if (pVNIC->hUnicast)
+ {
+ mac_unicast_remove(pVNIC->hClient, pVNIC->hUnicast);
+ pVNIC->hUnicast = NULL;
+ }
+#endif
+
+ if (pVNIC->hPromisc)
+ {
+ mac_promisc_remove(pVNIC->hPromisc);
+ pVNIC->hPromisc = NULL;
+ }
+
+ mac_rx_clear(pVNIC->hClient);
+
+ mac_client_close(pVNIC->hClient, 0 /* fFlags */);
+ pVNIC->hClient = NULL;
+ }
+
+ if (pVNIC->hInterface)
+ {
+ mac_close(pVNIC->hInterface);
+ pVNIC->hInterface = NULL;
+ }
+
+ if (pVNIC->fCreated)
+ {
+ vnic_delete(pVNIC->hLinkId, 0 /* Flags */);
+ pVNIC->hLinkId = DATALINK_INVALID_LINKID;
+ pVNIC->fCreated = false;
+ }
+
+ if (pVNIC->pVNICTemplate)
+ {
+ RTMemFree(pVNIC->pVNICTemplate);
+ pVNIC->pVNICTemplate = NULL;
+ }
+ }
+}
+
+
+/**
+ * Create a non-persistent VNIC over the given interface.
+ *
+ * @param pThis The VM connection instance.
+ * @param ppVNIC Where to store the created VNIC.
+ *
+ * @returns VBox status code.
+ */
+LOCAL int vboxNetFltSolarisCreateVNIC(PVBOXNETFLTINS pThis, PVBOXNETFLTVNIC *ppVNIC)
+{
+ Log((DEVICE_NAME ":vboxNetFltSolarisCreateVNIC pThis=%p\n", pThis));
+
+ AssertReturn(pThis, VERR_INVALID_POINTER);
+ AssertReturn(ppVNIC, VERR_INVALID_POINTER);
+
+ int rc = VERR_INVALID_STATE;
+ PVBOXNETFLTVNIC pVNIC = vboxNetFltSolarisAllocVNIC();
+ if (RT_UNLIKELY(!pVNIC))
+ return VERR_NO_MEMORY;
+
+ /*
+ * Set a random MAC address for now. It will be changed to the VM interface's
+ * MAC address later, see vboxNetFltPortOsNotifyMacAddress().
+ */
+ RTMAC GuestMac;
+ GuestMac.au8[0] = 0x08;
+ GuestMac.au8[1] = 0x00;
+ GuestMac.au8[2] = 0x27;
+ RTRandBytes(&GuestMac.au8[3], 3);
+
+ AssertCompile(sizeof(RTMAC) <= MAXMACADDRLEN);
+
+ const char *pszLinkName = pThis->szName;
+ uint16_t uVLANId = VLAN_ID_NONE;
+ vnic_mac_addr_type_t AddrType = VNIC_MAC_ADDR_TYPE_FIXED;
+ vnic_ioc_diag_t Diag = VNIC_IOC_DIAG_NONE;
+ int MacSlot = 0;
+ int MacLen = sizeof(GuestMac);
+ uint32_t fFlags = 0;
+
+ if (pThis->u.s.fIsVNICTemplate)
+ {
+ pVNIC->pVNICTemplate = RTMemAllocZ(sizeof(VBOXNETFLTVNICTEMPLATE));
+ if (RT_UNLIKELY(!pVNIC->pVNICTemplate))
+ {
+ vboxNetFltSolarisFreeVNIC(pVNIC);
+ return VERR_NO_MEMORY;
+ }
+
+ /*
+ * Initialize the VNIC template.
+ */
+ rc = vboxNetFltSolarisInitVNICTemplate(pThis, pVNIC->pVNICTemplate);
+ if (RT_FAILURE(rc))
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisCreateVNIC failed to initialize VNIC from VNIC template. rc=%Rrc\n", rc));
+ vboxNetFltSolarisFreeVNIC(pVNIC);
+ return rc;
+ }
+
+ pszLinkName = pVNIC->pVNICTemplate->szLinkName;
+ uVLANId = pVNIC->pVNICTemplate->uVLANId;
+#if 0
+ /*
+ * Required only if we're creating a VLAN interface & not a VNIC with a VLAN Id.
+ */
+ if (uVLANId != VLAN_ID_NONE)
+ fFlags |= MAC_VLAN;
+#endif
+ Log((DEVICE_NAME ":vboxNetFltSolarisCreateVNIC pThis=%p VLAN Id=%u\n", pThis, uVLANId));
+ }
+
+ /*
+ * Make sure the dynamic VNIC we're creating doesn't already exists, if so pick a new instance.
+ * This is to avoid conflicts with users manually creating VNICs whose name starts with VBOXBOW_VNIC_NAME.
+ */
+ do
+ {
+ AssertCompile(sizeof(pVNIC->szName) > sizeof(VBOXBOW_VNIC_NAME "18446744073709551615" /* UINT64_MAX */));
+ RTStrPrintf(pVNIC->szName, sizeof(pVNIC->szName), "%s%RU64", VBOXBOW_VNIC_NAME, g_VBoxNetFltSolarisVNICId);
+ mac_handle_t hTmpMacHandle;
+ rc = mac_open_by_linkname(pVNIC->szName, &hTmpMacHandle);
+ if (rc)
+ break;
+ mac_close(hTmpMacHandle);
+ ASMAtomicIncU64(&g_VBoxNetFltSolarisVNICId);
+ } while (1);
+
+ /*
+ * Create the VNIC under 'pszLinkName', which can be the one from the VNIC template or can
+ * be a physical interface.
+ */
+ rc = vnic_create(pVNIC->szName, pszLinkName, &AddrType, &MacLen, GuestMac.au8, &MacSlot, 0 /* Mac-Prefix Length */, uVLANId,
+ fFlags, &pVNIC->hLinkId, &Diag, NULL /* Reserved */);
+ if (!rc)
+ {
+ pVNIC->fCreated = true;
+ ASMAtomicIncU64(&g_VBoxNetFltSolarisVNICId);
+
+ /*
+ * Now try opening the created VNIC.
+ */
+ rc = mac_open_by_linkid(pVNIC->hLinkId, &pVNIC->hInterface);
+ if (!rc)
+ {
+ /*
+ * Initialize the VNIC from the physical interface or the VNIC template.
+ */
+ rc = vboxNetFltSolarisInitVNIC(pThis, pVNIC);
+ if (RT_SUCCESS(rc))
+ {
+ Log((DEVICE_NAME ":vboxNetFltSolarisCreateVNIC created VNIC '%s' over '%s' with random mac %.6Rhxs\n",
+ pVNIC->szName, pszLinkName, &GuestMac));
+ *ppVNIC = pVNIC;
+ return VINF_SUCCESS;
+ }
+
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisCreateVNIC vboxNetFltSolarisInitVNIC failed. rc=%d\n", rc));
+ mac_close(pVNIC->hInterface);
+ pVNIC->hInterface = NULL;
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisCreateVNIC logrel failed to open VNIC '%s' over '%s'. rc=%d\n", pVNIC->szName,
+ pThis->szName, rc));
+ rc = VERR_INTNET_FLT_VNIC_LINK_ID_NOT_FOUND;
+ }
+
+ vboxNetFltSolarisDestroyVNIC(pVNIC);
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisCreateVNIC failed to create VNIC '%s' over '%s' rc=%d Diag=%d\n", pVNIC->szName,
+ pszLinkName, rc, Diag));
+ rc = VERR_INTNET_FLT_VNIC_CREATE_FAILED;
+ }
+
+ vboxNetFltSolarisFreeVNIC(pVNIC);
+
+ return rc;
+}
+
+
+/**
+ * Wrapper for getting the datalink ID given the MAC name.
+ *
+ * @param pszMacName The MAC name.
+ * @param pLinkId Where to store the datalink ID.
+ *
+ * @returns VBox status code.
+ */
+DECLINLINE(int) vboxNetFltSolarisGetLinkId(const char *pszMacName, datalink_id_t *pLinkId)
+{
+ /*
+ * dls_mgmt_get_linkid() requires to be in a state to answer upcalls. We should always use this
+ * first before resorting to other means to retrieve the MAC name.
+ */
+ int rc = dls_mgmt_get_linkid(pszMacName, pLinkId);
+ if (rc)
+ rc = dls_devnet_macname2linkid(pszMacName, pLinkId);
+
+ if (RT_LIKELY(!rc))
+ return VINF_SUCCESS;
+
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisGetLinkId failed for '%s'. rc=%d\n", pszMacName, rc));
+ return RTErrConvertFromErrno(rc);
+}
+
+
+/**
+ * Set the promiscuous mode RX hook.
+ *
+ * @param pThis The VM connection instance.
+ * @param pVNIC Pointer to the VNIC.
+ *
+ * @returns VBox status code.
+ */
+DECLINLINE(int) vboxNetFltSolarisSetPromisc(PVBOXNETFLTINS pThis, PVBOXNETFLTVNIC pVNIC)
+{
+ int rc = VINF_SUCCESS;
+ if (!pVNIC->hPromisc)
+ {
+ rc = mac_promisc_add(pVNIC->hClient, MAC_CLIENT_PROMISC_FILTERED, vboxNetFltSolarisRecv, pThis, &pVNIC->hPromisc,
+ MAC_PROMISC_FLAGS_NO_TX_LOOP | MAC_PROMISC_FLAGS_VLAN_TAG_STRIP | MAC_PROMISC_FLAGS_NO_PHYS);
+ if (RT_UNLIKELY(rc))
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisSetPromisc failed. rc=%d\n", rc));
+ rc = RTErrConvertFromErrno(rc);
+ }
+ return rc;
+}
+
+
+/**
+ * Clear the promiscuous mode RX hook.
+ *
+ * @param pThis The VM connection instance.
+ * @param pVNIC Pointer to the VNIC.
+ */
+DECLINLINE(void) vboxNetFltSolarisRemovePromisc(PVBOXNETFLTINS pThis, PVBOXNETFLTVNIC pVNIC)
+{
+ if (pVNIC->hPromisc)
+ {
+ mac_promisc_remove(pVNIC->hPromisc);
+ pVNIC->hPromisc = NULL;
+ }
+}
+
+
+/* -=-=-=-=-=- Common Hooks -=-=-=-=-=- */
+
+
+void vboxNetFltPortOsSetActive(PVBOXNETFLTINS pThis, bool fActive)
+{
+ Log((DEVICE_NAME ":vboxNetFltPortOsSetActive pThis=%p fActive=%d\n", pThis, fActive));
+
+ /*
+ * Reactivate/quiesce the interface.
+ */
+ PVBOXNETFLTVNIC pVNIC = list_head(&pThis->u.s.hVNICs);
+ if (fActive)
+ {
+ for (; pVNIC != NULL; pVNIC = list_next(&pThis->u.s.hVNICs, pVNIC))
+ if (pVNIC->hClient)
+ {
+#if 0
+ mac_rx_set(pVNIC->hClient, vboxNetFltSolarisRecv, pThis);
+#endif
+ vboxNetFltSolarisSetPromisc(pThis, pVNIC);
+ }
+ }
+ else
+ {
+ for (; pVNIC != NULL; pVNIC = list_next(&pThis->u.s.hVNICs, pVNIC))
+ if (pVNIC->hClient)
+ {
+#if 0
+ mac_rx_clear(pVNIC->hClient);
+#endif
+ vboxNetFltSolarisRemovePromisc(pThis, pVNIC);
+ }
+ }
+}
+
+
+int vboxNetFltOsDisconnectIt(PVBOXNETFLTINS pThis)
+{
+ Log((DEVICE_NAME ":vboxNetFltOsDisconnectIt pThis=%p\n", pThis));
+ return VINF_SUCCESS;
+}
+
+
+int vboxNetFltOsConnectIt(PVBOXNETFLTINS pThis)
+{
+ Log((DEVICE_NAME ":vboxNetFltOsConnectIt pThis=%p\n", pThis));
+ return VINF_SUCCESS;
+}
+
+
+void vboxNetFltOsDeleteInstance(PVBOXNETFLTINS pThis)
+{
+ Log((DEVICE_NAME ":vboxNetFltOsDeleteInstance pThis=%p\n", pThis));
+
+ if (pThis->u.s.hNotify)
+ mac_notify_remove(pThis->u.s.hNotify, B_TRUE /* Wait */);
+
+ /*
+ * Destroy all managed VNICs. If a VNIC was passed to us, there
+ * will be only 1 item in the list, otherwise as many interfaces
+ * that were somehow not destroyed using DisconnectInterface() will be
+ * present.
+ */
+ PVBOXNETFLTVNIC pVNIC = NULL;
+ while ((pVNIC = list_remove_head(&pThis->u.s.hVNICs)) != NULL)
+ {
+ vboxNetFltSolarisDestroyVNIC(pVNIC);
+ vboxNetFltSolarisFreeVNIC(pVNIC);
+ }
+
+ list_destroy(&pThis->u.s.hVNICs);
+}
+
+
+int vboxNetFltOsInitInstance(PVBOXNETFLTINS pThis, void *pvContext)
+{
+ Log((DEVICE_NAME ":vboxNetFltOsInitInstance pThis=%p pvContext=%p\n", pThis, pvContext));
+
+ /*
+ * Figure out if the interface is a VNIC or a physical/etherstub/whatever NIC, then
+ * do the actual VNIC creation if necessary in vboxNetFltPortOsConnectInterface().
+ */
+ mac_handle_t hInterface;
+ int rc = mac_open_by_linkname(pThis->szName, &hInterface);
+ if (RT_LIKELY(!rc))
+ {
+ rc = mac_is_vnic(hInterface);
+ if (!rc)
+ {
+ Log((DEVICE_NAME ":vboxNetFltOsInitInstance pThis=%p physical interface '%s' detected.\n", pThis, pThis->szName));
+ pThis->u.s.fIsVNIC = false;
+ }
+ else
+ {
+ pThis->u.s.fIsVNIC = true;
+ if (RTStrNCmp(pThis->szName, VBOXBOW_VNIC_TEMPLATE_NAME, sizeof(VBOXBOW_VNIC_TEMPLATE_NAME) - 1) == 0)
+ {
+ Log((DEVICE_NAME ":vboxNetFltOsInitInstance pThis=%p VNIC template '%s' detected.\n", pThis, pThis->szName));
+ pThis->u.s.fIsVNICTemplate = true;
+ }
+ }
+
+ if ( pThis->u.s.fIsVNIC
+ && !pThis->u.s.fIsVNICTemplate)
+ Log((DEVICE_NAME ":vboxNetFltOsInitInstance pThis=%p VNIC '%s' detected.\n", pThis, pThis->szName));
+
+ /*
+ * Report info. (host MAC address, promiscuous, GSO capabilities etc.) to IntNet.
+ */
+ rc = vboxNetFltSolarisReportInfo(pThis, hInterface, pThis->u.s.fIsVNIC);
+ if (RT_FAILURE(rc))
+ LogRel((DEVICE_NAME ":vboxNetFltOsInitInstance failed to report info. rc=%d\n", rc));
+
+ mac_close(hInterface);
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltOsInitInstance failed to open link '%s'! rc=%d\n", pThis->szName, rc));
+ rc = VERR_INTNET_FLT_IF_FAILED;
+ }
+
+ return rc;
+}
+
+
+int vboxNetFltOsPreInitInstance(PVBOXNETFLTINS pThis)
+{
+ /*
+ * Init. the solaris specific data.
+ */
+ pThis->u.s.fIsVNIC = false;
+ pThis->u.s.fIsVNICTemplate = false;
+ list_create(&pThis->u.s.hVNICs, sizeof(VBOXNETFLTVNIC), offsetof(VBOXNETFLTVNIC, hNode));
+ pThis->u.s.hNotify = NULL;
+ RT_ZERO(pThis->u.s.MacAddr);
+ return VINF_SUCCESS;
+}
+
+
+bool vboxNetFltOsMaybeRediscovered(PVBOXNETFLTINS pThis)
+{
+ /*
+ * @todo Think about this.
+ */
+ return false;
+}
+
+
+int vboxNetFltPortOsXmit(PVBOXNETFLTINS pThis, void *pvIfData, PINTNETSG pSG, uint32_t fDst)
+{
+ /*
+ * Validate parameters.
+ */
+ PVBOXNETFLTVNIC pVNIC = pvIfData;
+ AssertPtrReturn(pVNIC, VERR_INVALID_POINTER);
+ AssertMsgReturn(pVNIC->u32Magic == VBOXNETFLTVNIC_MAGIC,
+ ("Invalid magic=%#x (expected %#x)\n", pVNIC->u32Magic, VBOXNETFLTVNIC_MAGIC),
+ VERR_INVALID_MAGIC);
+
+ /*
+ * Xmit the packet down the appropriate VNIC interface.
+ */
+ int rc = VINF_SUCCESS;
+ mblk_t *pMsg = vboxNetFltSolarisMBlkFromSG(pThis, pSG, fDst);
+ if (RT_LIKELY(pMsg))
+ {
+ Log((DEVICE_NAME ":vboxNetFltPortOsXmit pThis=%p cbData=%d\n", pThis, MBLKL(pMsg)));
+
+ mac_tx_cookie_t pXmitCookie = mac_tx(pVNIC->hClient, pMsg, 0 /* Hint */, MAC_DROP_ON_NO_DESC, NULL /* return message */);
+ if (RT_LIKELY(!pXmitCookie))
+ return VINF_SUCCESS;
+
+ pMsg = NULL;
+ rc = VERR_NET_IO_ERROR;
+ LogRel((DEVICE_NAME ":vboxNetFltPortOsXmit Xmit failed pVNIC=%p.\n", pVNIC));
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltPortOsXmit no memory for allocating Xmit packet.\n"));
+ rc = VERR_NO_MEMORY;
+ }
+
+ return rc;
+}
+
+
+void vboxNetFltPortOsNotifyMacAddress(PVBOXNETFLTINS pThis, void *pvIfData, PCRTMAC pMac)
+{
+ Log((DEVICE_NAME ":vboxNetFltPortOSNotifyMacAddress pszIf=%s pszVNIC=%s MAC=%.6Rhxs\n", pThis->szName,
+ ((PVBOXNETFLTVNIC)pvIfData)->szName, pMac));
+
+ /*
+ * Validate parameters.
+ */
+ PVBOXNETFLTVNIC pVNIC = pvIfData;
+ AssertPtrReturnVoid(pVNIC);
+ AssertMsgReturnVoid(pVNIC->u32Magic == VBOXNETFLTVNIC_MAGIC,
+ ("Invalid pVNIC=%p magic=%#x (expected %#x)\n", pvIfData, pVNIC->u32Magic, VBOXNETFLTVNIC_MAGIC));
+ AssertMsgReturnVoid(pVNIC->hLinkId != DATALINK_INVALID_LINKID,
+ ("Invalid hLinkId pVNIC=%p magic=%#x\n", pVNIC, pVNIC->u32Magic));
+
+ /*
+ * Set the MAC address of the VNIC to the one used by the VM interface.
+ */
+ uchar_t au8GuestMac[MAXMACADDRLEN];
+ bcopy(pMac->au8, au8GuestMac, sizeof(RTMAC));
+
+ vnic_mac_addr_type_t AddrType = VNIC_MAC_ADDR_TYPE_FIXED;
+ vnic_ioc_diag_t Diag = VNIC_IOC_DIAG_NONE;
+ int MacSlot = 0;
+ int MacLen = sizeof(RTMAC);
+
+ int rc = vnic_modify_addr(pVNIC->hLinkId, &AddrType, &MacLen, au8GuestMac, &MacSlot, 0 /* Mac-Prefix Length */, &Diag);
+ if (RT_LIKELY(!rc))
+ {
+ /*
+ * Remove existing unicast address, promisc. and the RX hook.
+ */
+#if 0
+ if (pVNIC->hUnicast)
+ {
+ mac_rx_clear(pVNIC->hClient);
+ mac_unicast_remove(pVNIC->hClient, pVNIC->hUnicast);
+ pVNIC->hUnicast = NULL;
+ }
+#endif
+
+ if (pVNIC->hPromisc)
+ {
+ mac_promisc_remove(pVNIC->hPromisc);
+ pVNIC->hPromisc = NULL;
+ }
+
+ mac_diag_t MacDiag = MAC_DIAG_NONE;
+ /* uint16_t uVLANId = pVNIC->pVNICTemplate ? pVNIC->pVNICTemplate->uVLANId : 0; */
+#if 0
+ rc = mac_unicast_add(pVNIC->hClient, NULL, MAC_UNICAST_PRIMARY, &pVNIC->hUnicast, 0 /* VLAN Id */, &MacDiag);
+#endif
+ if (RT_LIKELY(!rc))
+ {
+ rc = vboxNetFltSolarisSetPromisc(pThis, pVNIC);
+#if 0
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Set the RX receive function.
+ * This shouldn't be necessary as vboxNetFltPortOsSetActive() will be invoked after this, but in the future,
+ * if the guest NIC changes MAC address this may not be followed by a vboxNetFltPortOsSetActive() call,
+ * so set it here anyway.
+ */
+ mac_rx_set(pVNIC->hClient, vboxNetFltSolarisRecv, pThis);
+ Log((DEVICE_NAME ":vboxNetFltPortOsNotifyMacAddress successfully added unicast address %.6Rhxs\n", pMac));
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltPortOsNotifyMacAddress failed to set promiscuous mode. rc=%d\n", rc));
+ mac_unicast_remove(pVNIC->hClient, pVNIC->hUnicast);
+ pVNIC->hUnicast = NULL;
+#endif
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltPortOsNotifyMacAddress failed to add primary unicast address. rc=%d Diag=%d\n", rc,
+ MacDiag));
+ }
+ }
+ else
+ {
+ /*
+ * They really ought to use EEXIST, but I'm afraid this error comes from the VNIC device driver directly.
+ * Sequence: vnic_modify_addr()->mac_unicast_primary_set()->mac_update_macaddr() which uses a function pointer
+ * to the MAC driver (calls mac_vnic_unicast_set() in our case). Documented here if the error code should change we know
+ * where to look.
+ */
+ if (rc == ENOTSUP)
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltPortOsNotifyMacAddress: failed! a VNIC with mac %.6Rhxs probably already exists.",
+ pMac, rc));
+ LogRel((DEVICE_NAME ":vboxNetFltPortOsNotifyMacAddress: This NIC cannot establish connection. szName=%s szVNIC=%s\n",
+ pThis->szName, pVNIC->szName));
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltPortOsNotifyMacAddress failed! mac %.6Rhxs rc=%d Diag=%d\n", pMac, rc, Diag));
+ }
+}
+
+
+int vboxNetFltPortOsConnectInterface(PVBOXNETFLTINS pThis, void *pvIf, void **ppvIfData)
+{
+ Log((DEVICE_NAME ":vboxNetFltPortOsConnectInterface pThis=%p pvIf=%p\n", pThis, pvIf));
+
+ int rc = VINF_SUCCESS;
+
+ /*
+ * If the underlying interface is a physical interface or a VNIC template, we need to create
+ * a VNIC per guest NIC.
+ */
+ if ( !pThis->u.s.fIsVNIC
+ || pThis->u.s.fIsVNICTemplate)
+ {
+ PVBOXNETFLTVNIC pVNIC = NULL;
+ rc = vboxNetFltSolarisCreateVNIC(pThis, &pVNIC);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * VM Interface<->VNIC association so that we can Xmit/Recv on the right ones.
+ */
+ pVNIC->pvIf = pvIf;
+ *ppvIfData = pVNIC;
+
+ /*
+ * Add the created VNIC to the list of VNICs we manage.
+ */
+ list_insert_tail(&pThis->u.s.hVNICs, pVNIC);
+ return VINF_SUCCESS;
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltPortOsConnectInterface failed to create VNIC rc=%d\n", rc));
+ }
+ else
+ {
+ /*
+ * This is a VNIC passed to us, use it directly.
+ */
+ PVBOXNETFLTVNIC pVNIC = vboxNetFltSolarisAllocVNIC();
+ if (RT_LIKELY(pVNIC))
+ {
+ pVNIC->fCreated = false;
+
+ rc = mac_open_by_linkname(pThis->szName, &pVNIC->hInterface);
+ if (!rc)
+ {
+ /*
+ * Obtain the data link ID for this VNIC, it's needed for modifying the MAC address among other things.
+ */
+ rc = vboxNetFltSolarisGetLinkId(pThis->szName, &pVNIC->hLinkId);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Initialize the VNIC and add it to the list of managed VNICs.
+ */
+ RTStrPrintf(pVNIC->szName, sizeof(pVNIC->szName), "%s", pThis->szName);
+ rc = vboxNetFltSolarisInitVNIC(pThis, pVNIC);
+ if (!rc)
+ {
+ pVNIC->pvIf = pvIf;
+ *ppvIfData = pVNIC;
+ list_insert_head(&pThis->u.s.hVNICs, pVNIC);
+ return VINF_SUCCESS;
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltPortOsConnectInterface failed to initialize VNIC. rc=%d\n", rc));
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltPortOsConnectInterface failed to get link id for '%s'. rc=%d\n",
+ pThis->szName, rc));
+ }
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltPortOsConnectInterface failed to open VNIC '%s'. rc=%d\n", pThis->szName, rc));
+ rc = VERR_OPEN_FAILED;
+ }
+
+ vboxNetFltSolarisFreeVNIC(pVNIC);
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltOsInitInstance failed to allocate VNIC private data.\n"));
+ rc = VERR_NO_MEMORY;
+ }
+ }
+
+ return rc;
+}
+
+
+int vboxNetFltPortOsDisconnectInterface(PVBOXNETFLTINS pThis, void *pvIfData)
+{
+ Log((DEVICE_NAME ":vboxNetFltPortOsDisconnectInterface pThis=%p\n", pThis));
+
+ /*
+ * It is possible we get called when vboxNetFltPortOsConnectInterface() didn't succeed
+ * in which case pvIfData will be NULL. See intnetR0NetworkCreateIf() pfnConnectInterface call
+ * through reference counting in SUPR0ObjRelease() for the "pIf" object.
+ */
+ PVBOXNETFLTVNIC pVNIC = pvIfData;
+ if (RT_LIKELY(pVNIC))
+ {
+ AssertMsgReturn(pVNIC->u32Magic == VBOXNETFLTVNIC_MAGIC,
+ ("Invalid magic=%#x (expected %#x)\n", pVNIC->u32Magic, VBOXNETFLTVNIC_MAGIC), VERR_INVALID_POINTER);
+
+ /*
+ * If the underlying interface is a physical interface or a VNIC template, we need to delete the created VNIC.
+ */
+ if ( !pThis->u.s.fIsVNIC
+ || pThis->u.s.fIsVNICTemplate)
+ {
+ /*
+ * Remove the VNIC from the list, destroy and free it.
+ */
+ list_remove(&pThis->u.s.hVNICs, pVNIC);
+ Log((DEVICE_NAME ":vboxNetFltPortOsDisconnectInterface destroying pVNIC=%p\n", pVNIC));
+ vboxNetFltSolarisDestroyVNIC(pVNIC);
+ vboxNetFltSolarisFreeVNIC(pVNIC);
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/solaris/vboxbow.conf b/src/VBox/HostDrivers/VBoxNetFlt/solaris/vboxbow.conf
new file mode 100644
index 00000000..8b4f716d
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/solaris/vboxbow.conf
@@ -0,0 +1,43 @@
+# $Id: vboxbow.conf $
+## @file
+# Solaris Host VBoxBow (Crossbow) Configuration
+#
+
+#
+# Copyright (C) 2008-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+# 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="vboxbow" parent="pseudo" instance=0;
+
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/solaris/vboxflt.conf b/src/VBox/HostDrivers/VBoxNetFlt/solaris/vboxflt.conf
new file mode 100644
index 00000000..dce8b795
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/solaris/vboxflt.conf
@@ -0,0 +1,56 @@
+# $Id: vboxflt.conf $
+## @file
+# Solaris Host VBoxFlt Configuration
+#
+
+#
+# Copyright (C) 2008-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+# in the VirtualBox distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+#
+
+# 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="vboxflt" parent="pseudo" instance=0;
+
+# If the interface being used does not have IPv6 plumbed and you want
+# vboxflt to automatically attach to it by checking for Ipv6 stream every
+# "ipv6-pollinterval" seconds.
+#
+# By default this is disabled meaning if the interface does not have Ipv6
+# plumbed when the virtual machine starts, guest<->host Ipv6 will not work
+# though guest<->remote Ipv6 would work.
+#
+# Enable if you have a dynamically plumbing/unplumbing Ipv6 interface for
+# which you want vboxflt to adjust accordingly, otherwise keep this disabled.
+# Enabling this option will have some minor performance penalty.
+#ipv6-pollinterval=3;
+
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/Makefile.kup b/src/VBox/HostDrivers/VBoxNetFlt/win/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/win/Makefile.kup
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/cfg/Makefile.kup b/src/VBox/HostDrivers/VBoxNetFlt/win/cfg/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/win/cfg/Makefile.kup
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/cfg/VBoxNetCfg.cpp b/src/VBox/HostDrivers/VBoxNetFlt/win/cfg/VBoxNetCfg.cpp
new file mode 100644
index 00000000..a686196f
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/win/cfg/VBoxNetCfg.cpp
@@ -0,0 +1,3770 @@
+/* $Id: VBoxNetCfg.cpp $ */
+/** @file
+ * VBoxNetCfg.cpp - Network Configuration API.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define _WIN32_DCOM
+
+#include "VBox/VBoxNetCfg-win.h"
+#include "VBox/VBoxDrvCfg-win.h"
+
+#include <devguid.h>
+#include <regstr.h>
+#include <iprt/win/shlobj.h>
+#include <cfgmgr32.h>
+#include <iprt/win/objbase.h>
+
+#include <Wbemidl.h>
+
+#include <iprt/win/winsock2.h>
+#include <iprt/win/ws2tcpip.h>
+#include <ws2ipdef.h>
+#include <iprt/win/netioapi.h>
+#include <iprt/win/iphlpapi.h>
+
+#include <iprt/asm.h>
+#include <iprt/assertcompile.h>
+#include <iprt/mem.h>
+#include <iprt/list.h>
+#include <iprt/rand.h>
+#include <iprt/string.h>
+#include <iprt/utf16.h>
+#include <VBox/com/string.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#ifndef Assert /** @todo r=bird: where would this be defined? */
+//# ifdef DEBUG
+//# define Assert(_expr) assert(_expr)
+//# else
+//# define Assert(_expr) do{ }while (0)
+//# endif
+# define Assert _ASSERT
+# define AssertMsg(expr, msg) do{}while (0)
+#endif
+
+#define NonStandardLog DoLogging
+#define NonStandardLogFlow(x) DoLogging x
+
+#define SetErrBreak(strAndArgs) \
+ if (1) { \
+ hrc = E_FAIL; \
+ NonStandardLog strAndArgs; \
+ bstrError.printfNoThrow strAndArgs; \
+ break; \
+ } else do {} while (0)
+
+
+#define VBOXNETCFGWIN_NETADP_ID_SZ "sun_VBoxNetAdp"
+#define VBOXNETCFGWIN_NETADP_ID_WSZ RT_CONCAT(L,VBOXNETCFGWIN_NETADP_ID_SZ)
+#define DRIVERHWID VBOXNETCFGWIN_NETADP_ID_WSZ
+
+/* We assume the following name matches the device description in vboxnetadp6.inf */
+#define HOSTONLY_ADAPTER_NAME_SZ "VirtualBox Host-Only Ethernet Adapter"
+#define HOSTONLY_ADAPTER_NAME_WSZ RT_CONCAT(L,HOSTONLY_ADAPTER_NAME_SZ)
+
+#define VBOX_CONNECTION_NAME_SZ "VirtualBox Host-Only Network"
+#define VBOX_CONNECTION_NAME_WSZ RT_CONCAT(L,VBOX_CONNECTION_NAME_SZ)
+
+#define VBOXNETCFGWIN_NETLWF_ID L"oracle_VBoxNetLwf"
+
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static PFNVBOXNETCFGLOGGER volatile g_pfnLogger = NULL;
+
+/*
+ * Wrappers for HelpAPI functions
+ */
+typedef void FNINITIALIZEIPINTERFACEENTRY( _Inout_ PMIB_IPINTERFACE_ROW row);
+typedef FNINITIALIZEIPINTERFACEENTRY *PFNINITIALIZEIPINTERFACEENTRY;
+
+typedef NETIOAPI_API FNGETIPINTERFACEENTRY( _Inout_ PMIB_IPINTERFACE_ROW row);
+typedef FNGETIPINTERFACEENTRY *PFNGETIPINTERFACEENTRY;
+
+typedef NETIOAPI_API FNSETIPINTERFACEENTRY( _Inout_ PMIB_IPINTERFACE_ROW row);
+typedef FNSETIPINTERFACEENTRY *PFNSETIPINTERFACEENTRY;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static PFNINITIALIZEIPINTERFACEENTRY g_pfnInitializeIpInterfaceEntry = NULL;
+static PFNGETIPINTERFACEENTRY g_pfnGetIpInterfaceEntry = NULL;
+static PFNSETIPINTERFACEENTRY g_pfnSetIpInterfaceEntry = NULL;
+
+static void DoLogging(const char *pszString, ...);
+
+/*
+ * Forward declaration for using vboxNetCfgWinSetupMetric()
+ */
+static HRESULT vboxNetCfgWinSetupMetric(IN NET_LUID *pLuid);
+static HRESULT vboxNetCfgWinGetInterfaceLUID(IN HKEY hKey, OUT NET_LUID *pLUID);
+
+
+
+static HRESULT vboxNetCfgWinINetCfgLock(IN INetCfg *pNetCfg,
+ IN LPCWSTR pszwClientDescription,
+ IN DWORD cmsTimeout,
+ OUT LPWSTR *ppszwClientDescription)
+{
+ INetCfgLock *pLock;
+ HRESULT hr = pNetCfg->QueryInterface(IID_INetCfgLock, (PVOID *)&pLock);
+ if (FAILED(hr))
+ {
+ NonStandardLogFlow(("QueryInterface failed: %Rhrc\n", hr));
+ return hr;
+ }
+
+ hr = pLock->AcquireWriteLock(cmsTimeout, pszwClientDescription, ppszwClientDescription);
+ if (hr == S_FALSE)
+ NonStandardLogFlow(("Write lock busy\n"));
+ else if (FAILED(hr))
+ NonStandardLogFlow(("AcquireWriteLock failed: %Rhrc\n", hr));
+
+ pLock->Release();
+ return hr;
+}
+
+static HRESULT vboxNetCfgWinINetCfgUnlock(IN INetCfg *pNetCfg)
+{
+ INetCfgLock *pLock;
+ HRESULT hr = pNetCfg->QueryInterface(IID_INetCfgLock, (PVOID *)&pLock);
+ if (FAILED(hr))
+ {
+ NonStandardLogFlow(("QueryInterface failed: %Rhrc\n", hr));
+ return hr;
+ }
+
+ hr = pLock->ReleaseWriteLock();
+ if (FAILED(hr))
+ NonStandardLogFlow(("ReleaseWriteLock failed: %Rhrc\n", hr));
+
+ pLock->Release();
+ return hr;
+}
+
+VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinQueryINetCfg(OUT INetCfg **ppNetCfg,
+ IN BOOL fGetWriteLock,
+ IN LPCWSTR pszwClientDescription,
+ IN DWORD cmsTimeout,
+ OUT LPWSTR *ppszwClientDescription)
+{
+ INetCfg *pNetCfg = NULL;
+ HRESULT hr = CoCreateInstance(CLSID_CNetCfg, NULL, CLSCTX_INPROC_SERVER, IID_INetCfg, (PVOID *)&pNetCfg);
+ if (FAILED(hr))
+ {
+ NonStandardLogFlow(("CoCreateInstance failed: %Rhrc\n", hr));
+ return hr;
+ }
+
+ if (fGetWriteLock)
+ {
+ hr = vboxNetCfgWinINetCfgLock(pNetCfg, pszwClientDescription, cmsTimeout, ppszwClientDescription);
+ if (hr == S_FALSE)
+ {
+ NonStandardLogFlow(("Write lock is busy\n", hr));
+ hr = NETCFG_E_NO_WRITE_LOCK;
+ }
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ hr = pNetCfg->Initialize(NULL);
+ if (SUCCEEDED(hr))
+ {
+ *ppNetCfg = pNetCfg;
+ return S_OK;
+ }
+ NonStandardLogFlow(("Initialize failed: %Rhrc\n", hr));
+ }
+
+ pNetCfg->Release();
+ return hr;
+}
+
+VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinReleaseINetCfg(IN INetCfg *pNetCfg, IN BOOL fHasWriteLock)
+{
+ if (!pNetCfg) /* If network config has been released already, just bail out. */
+ {
+ NonStandardLogFlow(("Warning: No network config given but write lock is set to TRUE\n"));
+ return S_OK;
+ }
+
+ HRESULT hr = pNetCfg->Uninitialize();
+ if (FAILED(hr))
+ {
+ NonStandardLogFlow(("Uninitialize failed: %Rhrc\n", hr));
+ /* Try to release the write lock below. */
+ }
+
+ if (fHasWriteLock)
+ {
+ HRESULT hr2 = vboxNetCfgWinINetCfgUnlock(pNetCfg);
+ if (FAILED(hr2))
+ NonStandardLogFlow(("vboxNetCfgWinINetCfgUnlock failed: %Rhrc\n", hr2));
+ if (SUCCEEDED(hr))
+ hr = hr2;
+ }
+
+ pNetCfg->Release();
+ return hr;
+}
+
+static HRESULT vboxNetCfgWinGetComponentByGuidEnum(IEnumNetCfgComponent *pEnumNcc,
+ IN const GUID *pGuid,
+ OUT INetCfgComponent **ppNcc)
+{
+ HRESULT hr = pEnumNcc->Reset();
+ if (FAILED(hr))
+ {
+ NonStandardLogFlow(("Reset failed: %Rhrc\n", hr));
+ return hr;
+ }
+
+ INetCfgComponent *pNcc = NULL;
+ while ((hr = pEnumNcc->Next(1, &pNcc, NULL)) == S_OK)
+ {
+ ULONG uComponentStatus = 0;
+ hr = pNcc->GetDeviceStatus(&uComponentStatus);
+ if (SUCCEEDED(hr))
+ {
+ if (uComponentStatus == 0)
+ {
+ GUID NccGuid;
+ hr = pNcc->GetInstanceGuid(&NccGuid);
+
+ if (SUCCEEDED(hr))
+ {
+ if (NccGuid == *pGuid)
+ {
+ /* found the needed device */
+ *ppNcc = pNcc;
+ break;
+ }
+ }
+ else
+ NonStandardLogFlow(("GetInstanceGuid failed: %Rhrc\n", hr));
+ }
+ }
+
+ pNcc->Release();
+ }
+ return hr;
+}
+
+VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinGetComponentByGuid(IN INetCfg *pNc,
+ IN const GUID *pguidClass,
+ IN const GUID * pComponentGuid,
+ OUT INetCfgComponent **ppncc)
+{
+ IEnumNetCfgComponent *pEnumNcc = NULL;
+ HRESULT hr = pNc->EnumComponents(pguidClass, &pEnumNcc);
+ if (SUCCEEDED(hr))
+ {
+ hr = vboxNetCfgWinGetComponentByGuidEnum(pEnumNcc, pComponentGuid, ppncc);
+ if (hr == S_FALSE)
+ NonStandardLogFlow(("Component not found\n"));
+ else if (FAILED(hr))
+ NonStandardLogFlow(("vboxNetCfgWinGetComponentByGuidEnum failed: %Rhrc\n", hr));
+ pEnumNcc->Release();
+ }
+ else
+ NonStandardLogFlow(("EnumComponents failed: %Rhrc\n", hr));
+ return hr;
+}
+
+static HRESULT vboxNetCfgWinQueryInstaller(IN INetCfg *pNetCfg, IN const GUID *pguidClass, INetCfgClassSetup **ppSetup)
+{
+ HRESULT hr = pNetCfg->QueryNetCfgClass(pguidClass, IID_INetCfgClassSetup, (void **)ppSetup);
+ if (FAILED(hr))
+ NonStandardLogFlow(("QueryNetCfgClass failed: %Rhrc\n", hr));
+ return hr;
+}
+
+VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinInstallComponent(IN INetCfg *pNetCfg, IN LPCWSTR pszwComponentId,
+ IN const GUID *pguidClass, OUT INetCfgComponent **ppComponent)
+{
+ INetCfgClassSetup *pSetup;
+ HRESULT hr = vboxNetCfgWinQueryInstaller(pNetCfg, pguidClass, &pSetup);
+ if (FAILED(hr))
+ {
+ NonStandardLogFlow(("vboxNetCfgWinQueryInstaller failed: %Rhrc\n", hr));
+ return hr;
+ }
+
+ OBO_TOKEN Token;
+ RT_ZERO(Token);
+ Token.Type = OBO_USER;
+
+ INetCfgComponent *pTempComponent = NULL;
+ hr = pSetup->Install(pszwComponentId, &Token,
+ 0, /* IN DWORD dwSetupFlags */
+ 0, /* IN DWORD dwUpgradeFromBuildNo */
+ NULL, /* IN LPCWSTR pszwAnswerFile */
+ NULL, /* IN LPCWSTR pszwAnswerSections */
+ &pTempComponent);
+ if (SUCCEEDED(hr))
+ {
+ if (pTempComponent != NULL)
+ {
+ /*
+ * Set default metric value of interface to fix multicast issue
+ * See @bugref{6379} for details.
+ */
+ HKEY hKey = (HKEY)INVALID_HANDLE_VALUE;
+ HRESULT hrc2 = pTempComponent->OpenParamKey(&hKey);
+
+ /* Set default metric value for host-only interface only */
+ if ( SUCCEEDED(hrc2)
+ && hKey != (HKEY)INVALID_HANDLE_VALUE
+ /* Original was weird: && wcsnicmp(pszwComponentId, VBOXNETCFGWIN_NETADP_ID_WSZ, 256) == 0) */
+ && RTUtf16ICmpAscii(pszwComponentId, VBOXNETCFGWIN_NETADP_ID_SZ) == 0)
+ {
+ NET_LUID luid;
+ hrc2 = vboxNetCfgWinGetInterfaceLUID(hKey, &luid);
+
+ /* Close the key as soon as possible. See @bugref{7973}. */
+ RegCloseKey(hKey);
+ hKey = (HKEY)INVALID_HANDLE_VALUE;
+
+ if (FAILED(hrc2))
+ {
+ /*
+ * The setting of Metric is not very important functionality,
+ * So we will not break installation process due to this error.
+ */
+ NonStandardLogFlow(("VBoxNetCfgWinInstallComponent Warning! vboxNetCfgWinGetInterfaceLUID failed, default metric for new interface will not be set: %Rhrc\n", hrc2));
+ }
+ else
+ {
+ hrc2 = vboxNetCfgWinSetupMetric(&luid);
+ if (FAILED(hrc2))
+ {
+ /*
+ * The setting of Metric is not very important functionality,
+ * So we will not break installation process due to this error.
+ */
+ NonStandardLogFlow(("VBoxNetCfgWinInstallComponent Warning! vboxNetCfgWinSetupMetric failed, default metric for new interface will not be set: %Rhrc\n", hrc2));
+ }
+ }
+ }
+ if (hKey != (HKEY)INVALID_HANDLE_VALUE)
+ RegCloseKey(hKey);
+ if (ppComponent != NULL)
+ *ppComponent = pTempComponent;
+ else
+ pTempComponent->Release();
+ }
+
+ /* ignore the apply failure */
+ HRESULT hrc3 = pNetCfg->Apply();
+ Assert(hrc3 == S_OK);
+ if (hrc3 != S_OK)
+ NonStandardLogFlow(("Apply failed: %Rhrc\n", hrc3));
+ }
+ else
+ NonStandardLogFlow(("Install failed: %Rhrc\n", hr));
+
+ pSetup->Release();
+ return hr;
+}
+
+static HRESULT vboxNetCfgWinInstallInfAndComponent(IN INetCfg *pNetCfg, IN LPCWSTR pszwComponentId, IN const GUID *pguidClass,
+ IN LPCWSTR const *apwszInfPaths, IN UINT cInfPaths,
+ OUT INetCfgComponent **ppComponent)
+{
+ NonStandardLogFlow(("Installing %u INF files ...\n", cInfPaths));
+
+ HRESULT hr = S_OK;
+ UINT cFilesProcessed = 0;
+ for (; cFilesProcessed < cInfPaths; cFilesProcessed++)
+ {
+ NonStandardLogFlow(("Installing INF file \"%ls\" ...\n", apwszInfPaths[cFilesProcessed]));
+ hr = VBoxDrvCfgInfInstall(apwszInfPaths[cFilesProcessed]);
+ if (FAILED(hr))
+ {
+ NonStandardLogFlow(("VBoxNetCfgWinInfInstall failed: %Rhrc\n", hr));
+ break;
+ }
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ hr = VBoxNetCfgWinInstallComponent(pNetCfg, pszwComponentId, pguidClass, ppComponent);
+ if (FAILED(hr))
+ NonStandardLogFlow(("VBoxNetCfgWinInstallComponent failed: %Rhrc\n", hr));
+ }
+
+ if (FAILED(hr))
+ {
+ NonStandardLogFlow(("Installation failed, rolling back installation set ...\n"));
+
+ do
+ {
+ HRESULT hr2 = VBoxDrvCfgInfUninstall(apwszInfPaths[cFilesProcessed], 0);
+ if (FAILED(hr2))
+ NonStandardLogFlow(("VBoxDrvCfgInfUninstall failed: %Rhrc\n", hr2));
+ /* Keep going. */
+ if (!cFilesProcessed)
+ break;
+ } while (cFilesProcessed--);
+
+ NonStandardLogFlow(("Rollback complete\n"));
+ }
+
+ return hr;
+}
+
+VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinUninstallComponent(IN INetCfg *pNetCfg, IN INetCfgComponent *pComponent)
+{
+ GUID GuidClass;
+ HRESULT hr = pComponent->GetClassGuid(&GuidClass);
+ if (FAILED(hr))
+ {
+ NonStandardLogFlow(("GetClassGuid failed: %Rhrc\n", hr));
+ return hr;
+ }
+
+ INetCfgClassSetup *pSetup = NULL;
+ hr = vboxNetCfgWinQueryInstaller(pNetCfg, &GuidClass, &pSetup);
+ if (FAILED(hr))
+ {
+ NonStandardLogFlow(("vboxNetCfgWinQueryInstaller failed: %Rhrc\n", hr));
+ return hr;
+ }
+
+ OBO_TOKEN Token;
+ RT_ZERO(Token);
+ Token.Type = OBO_USER;
+
+ hr = pSetup->DeInstall(pComponent, &Token, NULL /* OUT LPWSTR *pmszwRefs */);
+ if (SUCCEEDED(hr))
+ {
+ hr = pNetCfg->Apply();
+ if (FAILED(hr))
+ NonStandardLogFlow(("Apply failed: %Rhrc\n", hr));
+ }
+ else
+ NonStandardLogFlow(("DeInstall failed: %Rhrc\n", hr));
+
+ if (pSetup)
+ pSetup->Release();
+ return hr;
+}
+
+typedef BOOL (*PFN_VBOXNETCFGWIN_NETCFGENUM_CALLBACK_T)(IN INetCfg *pNetCfg, IN INetCfgComponent *pNetCfgComponent,
+ PVOID pvContext);
+
+static HRESULT vboxNetCfgWinEnumNetCfgComponents(IN INetCfg *pNetCfg,
+ IN const GUID *pguidClass,
+ PFN_VBOXNETCFGWIN_NETCFGENUM_CALLBACK_T pfnCallback,
+ PVOID pContext)
+{
+ IEnumNetCfgComponent *pEnumComponent = NULL;
+ HRESULT hr = pNetCfg->EnumComponents(pguidClass, &pEnumComponent);
+ if (SUCCEEDED(hr))
+ {
+ INetCfgComponent *pNetCfgComponent;
+ hr = pEnumComponent->Reset();
+ for (;;)
+ {
+ hr = pEnumComponent->Next(1, &pNetCfgComponent, NULL);
+ if (hr == S_OK)
+ {
+// ULONG uComponentStatus;
+// hr = pNcc->GetDeviceStatus(&uComponentStatus);
+// if (SUCCEEDED(hr))
+ BOOL fResult = FALSE;
+ if (pNetCfgComponent)
+ {
+ fResult = pfnCallback(pNetCfg, pNetCfgComponent, pContext);
+ pNetCfgComponent->Release();
+ }
+
+ if (!fResult)
+ break;
+ }
+ else
+ {
+ if (hr == S_FALSE)
+ hr = S_OK; /* no more components */
+ else
+ NonStandardLogFlow(("Next failed: %Rhrc\n", hr));
+ break;
+ }
+ }
+ pEnumComponent->Release();
+ }
+ return hr;
+}
+
+/** PFNVBOXNETCFGWINNETENUMCALLBACK */
+static BOOL vboxNetCfgWinRemoveAllNetDevicesOfIdCallback(HDEVINFO hDevInfo, PSP_DEVINFO_DATA pDev, PVOID pvContext)
+{
+ RT_NOREF1(pvContext);
+
+ SP_REMOVEDEVICE_PARAMS rmdParams;
+ RT_ZERO(rmdParams);
+ rmdParams.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER);
+ rmdParams.ClassInstallHeader.InstallFunction = DIF_REMOVE;
+ rmdParams.Scope = DI_REMOVEDEVICE_GLOBAL;
+
+ if (SetupDiSetClassInstallParams(hDevInfo,pDev,
+ &rmdParams.ClassInstallHeader, sizeof(rmdParams)))
+ {
+ if (SetupDiSetSelectedDevice(hDevInfo, pDev))
+ {
+#ifndef VBOXNETCFG_DELAYEDRENAME
+ /* Figure out NetCfgInstanceId. */
+ HKEY hKey = SetupDiOpenDevRegKey(hDevInfo,
+ pDev,
+ DICS_FLAG_GLOBAL,
+ 0,
+ DIREG_DRV,
+ KEY_READ);
+ if (hKey == INVALID_HANDLE_VALUE)
+ NonStandardLogFlow(("vboxNetCfgWinRemoveAllNetDevicesOfIdCallback: SetupDiOpenDevRegKey failed with error %u\n",
+ GetLastError()));
+ else
+ {
+ WCHAR wszCfgGuidString[50] = { L'' };
+ DWORD cbSize = sizeof(wszCfgGuidString) - sizeof(WCHAR); /* make sure we get a terminated string back */
+ DWORD dwValueType = 0;
+ LSTATUS lrc = RegQueryValueExW(hKey, L"NetCfgInstanceId", NULL, &dwValueType, (LPBYTE)wszCfgGuidString, &cbSize);
+ if (lrc == ERROR_SUCCESS)
+ {
+ /** @todo r=bird: original didn't check the type here, just assumed it was a
+ * valid zero terminated string. (zero term handled by -sizeof(WHCAR) above now). */
+ if (dwValueType == REG_SZ || dwValueType == REG_EXPAND_SZ || dwValueType == REG_EXPAND_SZ)
+ {
+ NonStandardLogFlow(("vboxNetCfgWinRemoveAllNetDevicesOfIdCallback: Processing device ID \"%ls\"\n",
+ wszCfgGuidString));
+
+ /* Figure out device name. */
+ WCHAR wszDevName[256 + 1] = {0};
+ if (SetupDiGetDeviceRegistryPropertyW(hDevInfo, pDev, SPDRP_FRIENDLYNAME, NULL, (PBYTE)wszDevName,
+ sizeof(wszDevName) - sizeof(WCHAR) /* yes, in bytes */, NULL))
+ {
+ /*
+ * Rename the connection before removing the device. This will
+ * hopefully prevent an error when we will be attempting
+ * to rename a newly created connection (see @bugref{6740}).
+ */
+ WCHAR wszNewName[RT_ELEMENTS(wszDevName) + 128 /* ensure sufficient buffer */];
+ HRESULT hr = VBoxNetCfgWinGenHostonlyConnectionName(wszDevName, wszNewName,
+ RT_ELEMENTS(wszNewName) - 10 /*removed++*/,
+ NULL);
+ RTUtf16CatAscii(wszNewName, sizeof(wszNewName), " removed");
+ if (SUCCEEDED(hr))
+ hr = VBoxNetCfgWinRenameConnection(wszCfgGuidString, wszNewName);
+ //NonStandardLogFlow(("VBoxNetCfgWinRenameConnection(%S,%S) => 0x%x\n", wszCfgGuidString, TempName, hr_tmp));
+ }
+ else
+ NonStandardLogFlow(("vboxNetCfgWinRemoveAllNetDevicesOfIdCallback: Failed to get friendly name for device \"%ls\"\n",
+ wszCfgGuidString));
+ }
+ else
+ NonStandardLogFlow(("vboxNetCfgWinRemoveAllNetDevicesOfIdCallback: Friendly name for \"%S\" isn't a string: %d\n",
+ wszCfgGuidString, dwValueType
+ }
+ else
+ NonStandardLogFlow(("vboxNetCfgWinRemoveAllNetDevicesOfIdCallback: Querying instance ID failed with %u (%#x)\n",
+ lrc, lrc));
+
+ RegCloseKey(hKey);
+ }
+#endif /* VBOXNETCFG_DELAYEDRENAME */
+
+ if (SetupDiCallClassInstaller(DIF_REMOVE, hDevInfo, pDev))
+ {
+ SP_DEVINSTALL_PARAMS_W DevParams = { sizeof(DevParams) };
+ if (SetupDiGetDeviceInstallParams(hDevInfo, pDev, &DevParams))
+ {
+ if ( (DevParams.Flags & DI_NEEDRESTART)
+ || (DevParams.Flags & DI_NEEDREBOOT))
+ NonStandardLog(("vboxNetCfgWinRemoveAllNetDevicesOfIdCallback: A reboot is required\n"));
+ }
+ else
+ NonStandardLogFlow(("vboxNetCfgWinRemoveAllNetDevicesOfIdCallback: SetupDiGetDeviceInstallParams failed with %u\n",
+ GetLastError()));
+ }
+ else
+ NonStandardLogFlow(("vboxNetCfgWinRemoveAllNetDevicesOfIdCallback: SetupDiCallClassInstaller failed with %u\n",
+ GetLastError()));
+ }
+ else
+ NonStandardLogFlow(("vboxNetCfgWinRemoveAllNetDevicesOfIdCallback: SetupDiSetSelectedDevice failed with %u\n",
+ GetLastError()));
+ }
+ else
+ NonStandardLogFlow(("vboxNetCfgWinRemoveAllNetDevicesOfIdCallback: SetupDiSetClassInstallParams failed with %u\n",
+ GetLastError()));
+
+ /* Continue enumeration. */
+ return TRUE;
+}
+
+typedef struct VBOXNECTFGWINPROPCHANGE
+{
+ VBOXNECTFGWINPROPCHANGE_TYPE_T enmPcType;
+ HRESULT hr;
+} VBOXNECTFGWINPROPCHANGE, *PVBOXNECTFGWINPROPCHANGE;
+
+static BOOL vboxNetCfgWinPropChangeAllNetDevicesOfIdCallback(HDEVINFO hDevInfo, PSP_DEVINFO_DATA pDev, PVOID pContext)
+{
+ PVBOXNECTFGWINPROPCHANGE pPc = (PVBOXNECTFGWINPROPCHANGE)pContext;
+
+ SP_PROPCHANGE_PARAMS PcParams;
+ RT_ZERO(PcParams);
+ PcParams.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER);
+ PcParams.ClassInstallHeader.InstallFunction = DIF_PROPERTYCHANGE;
+ PcParams.Scope = DICS_FLAG_GLOBAL;
+
+ switch (pPc->enmPcType)
+ {
+ case VBOXNECTFGWINPROPCHANGE_TYPE_DISABLE:
+ PcParams.StateChange = DICS_DISABLE;
+ NonStandardLogFlow(("vboxNetCfgWinPropChangeAllNetDevicesOfIdCallback: Change type (DICS_DISABLE): %d\n", pPc->enmPcType));
+ break;
+ case VBOXNECTFGWINPROPCHANGE_TYPE_ENABLE:
+ PcParams.StateChange = DICS_ENABLE;
+ NonStandardLogFlow(("vboxNetCfgWinPropChangeAllNetDevicesOfIdCallback: Change type (DICS_ENABLE): %d\n", pPc->enmPcType));
+ break;
+ default:
+ NonStandardLogFlow(("vboxNetCfgWinPropChangeAllNetDevicesOfIdCallback: Unexpected prop change type: %d\n", pPc->enmPcType));
+ pPc->hr = E_INVALIDARG;
+ return FALSE;
+ }
+
+ if (SetupDiSetClassInstallParamsW(hDevInfo, pDev, &PcParams.ClassInstallHeader, sizeof(PcParams)))
+ {
+ if (SetupDiSetSelectedDevice(hDevInfo, pDev))
+ {
+ if (SetupDiCallClassInstaller(DIF_PROPERTYCHANGE, hDevInfo, pDev))
+ {
+ SP_DEVINSTALL_PARAMS_W DevParams = { sizeof(DevParams) };
+ if (SetupDiGetDeviceInstallParamsW(hDevInfo, pDev, &DevParams))
+ {
+ if ( (DevParams.Flags & DI_NEEDRESTART)
+ || (DevParams.Flags & DI_NEEDREBOOT))
+ NonStandardLog(("vboxNetCfgWinPropChangeAllNetDevicesOfIdCallback: A reboot is required\n"));
+ }
+ else
+ NonStandardLogFlow(("vboxNetCfgWinPropChangeAllNetDevicesOfIdCallback: SetupDiGetDeviceInstallParams failed with %u\n",
+ GetLastError()));
+ }
+ else
+ NonStandardLogFlow(("vboxNetCfgWinPropChangeAllNetDevicesOfIdCallback: SetupDiCallClassInstaller failed with %u\n",
+ GetLastError()));
+ }
+ else
+ NonStandardLogFlow(("SetupDiSetSelectedDevice failed with %u\n", GetLastError()));
+ }
+ else
+ NonStandardLogFlow(("SetupDiSetClassInstallParams failed with %u\n", GetLastError()));
+
+ /* Continue enumeration. */
+ return TRUE;
+}
+
+typedef BOOL (*PFNVBOXNETCFGWINNETENUMCALLBACK)(HDEVINFO hDevInfo, PSP_DEVINFO_DATA pDev, PVOID pContext);
+
+static HRESULT vboxNetCfgWinEnumNetDevices(LPCWSTR pwszPnPId, PFNVBOXNETCFGWINNETENUMCALLBACK pfnCallback, PVOID pvContext)
+{
+ NonStandardLogFlow(("VBoxNetCfgWinEnumNetDevices: Searching for: %ls\n", pwszPnPId));
+
+ HRESULT hr;
+ HDEVINFO hDevInfo = SetupDiGetClassDevsExW(&GUID_DEVCLASS_NET,
+ NULL, /* IN PCTSTR Enumerator, OPTIONAL */
+ NULL, /* IN HWND hwndParent, OPTIONAL */
+ DIGCF_PRESENT, /* IN DWORD Flags,*/
+ NULL, /* IN HDEVINFO DeviceInfoSet, OPTIONAL */
+ NULL, /* IN PCTSTR MachineName, OPTIONAL */
+ NULL /* IN PVOID Reserved */);
+ if (hDevInfo != INVALID_HANDLE_VALUE)
+ {
+ size_t const cwcPnPId = RTUtf16Len(pwszPnPId);
+ DWORD winEr = NO_ERROR;
+ DWORD dwDevId = 0;
+ DWORD cbBuffer = 0;
+ PBYTE pbBuffer = NULL;
+ for (;;)
+ {
+ SP_DEVINFO_DATA Dev;
+ memset(&Dev, 0, sizeof(SP_DEVINFO_DATA));
+ Dev.cbSize = sizeof(SP_DEVINFO_DATA);
+
+ if (!SetupDiEnumDeviceInfo(hDevInfo, dwDevId, &Dev))
+ {
+ winEr = GetLastError();
+ if (winEr == ERROR_NO_MORE_ITEMS)
+ winEr = NO_ERROR;
+ break;
+ }
+
+ NonStandardLogFlow(("VBoxNetCfgWinEnumNetDevices: Enumerating device %u ... \n", dwDevId));
+ dwDevId++;
+
+ DWORD cbRequired = 0;
+ SetLastError(0);
+ if (!SetupDiGetDeviceRegistryPropertyW(hDevInfo, &Dev,
+ SPDRP_HARDWAREID, /* IN DWORD Property */
+ NULL, /* OUT PDWORD PropertyRegDataType OPTIONAL */
+ pbBuffer, /* OUT PBYTE PropertyBuffer */
+ cbBuffer, /* IN DWORD PropertyBufferSize */
+ &cbRequired /* OUT PDWORD RequiredSize OPTIONAL */))
+ {
+ winEr = GetLastError();
+ if (winEr != ERROR_INSUFFICIENT_BUFFER)
+ {
+ if (winEr == ERROR_INVALID_DATA)
+ {
+ NonStandardLogFlow(("VBoxNetCfgWinEnumNetDevices: SetupDiGetDeviceRegistryPropertyW (1) failed with ERROR_INVALID_DATA - ignoring, skipping to next device\n"));
+ continue;
+ }
+ NonStandardLogFlow(("VBoxNetCfgWinEnumNetDevices: SetupDiGetDeviceRegistryPropertyW (1) failed with %u\n", winEr));
+ break;
+ }
+ winEr = NO_ERROR;
+
+ cbBuffer = RT_ALIGN_32(cbRequired, 64);
+ void *pvNew = RTMemRealloc(pbBuffer, cbBuffer);
+ if (pvNew)
+ pbBuffer = (PBYTE)pvNew;
+ else
+ {
+ NonStandardLogFlow(("VBoxNetCfgWinEnumNetDevices: Out of memory allocating %u bytes\n", cbBuffer));
+ winEr = ERROR_OUTOFMEMORY;
+ break;
+ }
+
+ if (!SetupDiGetDeviceRegistryPropertyW(hDevInfo, &Dev,
+ SPDRP_HARDWAREID, /* IN DWORD Property */
+ NULL, /* OUT PDWORD PropertyRegDataType, OPTIONAL */
+ pbBuffer, /* OUT PBYTE PropertyBuffer */
+ cbBuffer, /* IN DWORD PropertyBufferSize */
+ &cbRequired /* OUT PDWORD RequiredSize OPTIONAL */))
+ {
+ winEr = GetLastError();
+ NonStandardLogFlow(("VBoxNetCfgWinEnumNetDevices: SetupDiGetDeviceRegistryPropertyW (2) failed with %u\n",
+ winEr));
+ break;
+ }
+ }
+
+ PWSTR pwszCurId = (PWSTR)pbBuffer;
+ size_t cwcCurId = RTUtf16Len(pwszCurId);
+
+ NonStandardLogFlow(("VBoxNetCfgWinEnumNetDevices: Device %u: %ls\n", dwDevId, pwszCurId));
+
+ if (cwcCurId >= cwcPnPId)
+ {
+ NonStandardLogFlow(("!RTUtf16NICmp(pwszCurId = (%ls), pwszPnPId = (%ls), cwcPnPId = (%d))\n", pwszCurId, pwszPnPId, cwcPnPId));
+
+ pwszCurId += cwcCurId - cwcPnPId;
+ if (!RTUtf16NICmp(pwszCurId, pwszPnPId, cwcPnPId))
+ {
+ if (!pfnCallback(hDevInfo, &Dev, pvContext))
+ break;
+ }
+ }
+ }
+
+ NonStandardLogFlow(("VBoxNetCfgWinEnumNetDevices: Found %u devices total\n", dwDevId));
+
+ if (pbBuffer)
+ RTMemFree(pbBuffer);
+
+ hr = HRESULT_FROM_WIN32(winEr);
+
+ SetupDiDestroyDeviceInfoList(hDevInfo);
+ }
+ else
+ {
+ DWORD winEr = GetLastError();
+ NonStandardLogFlow(("VBoxNetCfgWinEnumNetDevices: SetupDiGetClassDevsExW failed with %u\n", winEr));
+ hr = HRESULT_FROM_WIN32(winEr);
+ }
+
+ NonStandardLogFlow(("VBoxNetCfgWinEnumNetDevices: Ended with hr (0x%x)\n", hr));
+ return hr;
+}
+
+VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinRemoveAllNetDevicesOfId(IN LPCWSTR pwszPnPId)
+{
+ return vboxNetCfgWinEnumNetDevices(pwszPnPId, vboxNetCfgWinRemoveAllNetDevicesOfIdCallback, NULL);
+}
+
+VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinPropChangeAllNetDevicesOfId(IN LPCWSTR pwszPnPId, VBOXNECTFGWINPROPCHANGE_TYPE_T enmPcType)
+{
+ VBOXNECTFGWINPROPCHANGE Pc;
+ Pc.enmPcType = enmPcType;
+ Pc.hr = S_OK;
+ NonStandardLogFlow(("Calling VBoxNetCfgWinEnumNetDevices with pwszPnPId (= %ls) and vboxNetCfgWinPropChangeAllNetDevicesOfIdCallback\n", pwszPnPId));
+
+ HRESULT hr = vboxNetCfgWinEnumNetDevices(pwszPnPId, vboxNetCfgWinPropChangeAllNetDevicesOfIdCallback, &Pc);
+ if (!SUCCEEDED(hr))
+ {
+ NonStandardLogFlow(("VBoxNetCfgWinEnumNetDevices failed 0x%x\n", hr));
+ return hr;
+ }
+
+ if (!SUCCEEDED(Pc.hr))
+ {
+ NonStandardLogFlow(("vboxNetCfgWinPropChangeAllNetDevicesOfIdCallback failed 0x%x\n", Pc.hr));
+ return Pc.hr;
+ }
+
+ return S_OK;
+}
+
+
+
+/*********************************************************************************************************************************
+* Logging *
+*********************************************************************************************************************************/
+
+static void DoLogging(const char *pszString, ...)
+{
+ PFNVBOXNETCFGLOGGER pfnLogger = g_pfnLogger;
+ if (pfnLogger)
+ {
+ char szBuffer[4096];
+ va_list va;
+ va_start(va, pszString);
+ RTStrPrintfV(szBuffer, RT_ELEMENTS(szBuffer), pszString, va);
+ va_end(va);
+
+ pfnLogger(szBuffer);
+ }
+}
+
+VBOXNETCFGWIN_DECL(void) VBoxNetCfgWinSetLogging(IN PFNVBOXNETCFGLOGGER pfnLogger)
+{
+ g_pfnLogger = pfnLogger;
+}
+
+
+
+/*********************************************************************************************************************************
+* IP configuration API *
+*********************************************************************************************************************************/
+/* network settings config */
+#if 1 /** @todo r=bird: Can't we replace this with VBox/com/ptr.h? */
+/**
+ * Strong referencing operators. Used as a second argument to ComPtr<>/ComObjPtr<>.
+ */
+template<class C>
+class ComStrongRef
+{
+protected:
+
+ static void addref(C *p) { p->AddRef(); }
+ static void release(C *p) { p->Release(); }
+};
+
+
+/**
+ * Base template for smart COM pointers. Not intended to be used directly.
+ */
+template<class C, template<class> class RefOps = ComStrongRef>
+class ComPtrBase : protected RefOps<C>
+{
+public:
+
+ /* special template to disable AddRef()/Release() */
+ template<class I>
+ class NoAddRefRelease : public I
+ {
+ public:
+ virtual ~NoAddRefRelease() { /* Make VC++ 19.2 happy. */ }
+ private:
+#ifndef VBOX_WITH_XPCOM
+ STDMETHOD_(ULONG, AddRef)() = 0;
+ STDMETHOD_(ULONG, Release)() = 0;
+#else
+ NS_IMETHOD_(nsrefcnt) AddRef(void) = 0;
+ NS_IMETHOD_(nsrefcnt) Release(void) = 0;
+#endif
+ };
+
+protected:
+
+ ComPtrBase() : p(NULL) {}
+ ComPtrBase(const ComPtrBase &that) : p(that.p) { addref(); }
+ ComPtrBase(C *that_p) : p(that_p) { addref(); }
+
+ ~ComPtrBase() { release(); }
+
+ ComPtrBase &operator=(const ComPtrBase &that)
+ {
+ safe_assign(that.p);
+ return *this;
+ }
+
+ ComPtrBase &operator=(C *that_p)
+ {
+ safe_assign(that_p);
+ return *this;
+ }
+
+public:
+
+ void setNull()
+ {
+ release();
+ p = NULL;
+ }
+
+ bool isNull() const
+ {
+ return (p == NULL);
+ }
+
+ bool operator!() const { return isNull(); }
+
+ bool operator<(C* that_p) const { return p < that_p; }
+ bool operator==(C* that_p) const { return p == that_p; }
+
+ template<class I>
+ bool equalsTo(I *aThat) const
+ {
+ return ComPtrEquals(p, aThat);
+ }
+
+ template<class OC>
+ bool equalsTo(const ComPtrBase<OC> &oc) const
+ {
+ return equalsTo((OC *) oc);
+ }
+
+ /** Intended to pass instances as in parameters to interface methods */
+ operator C *() const { return p; }
+
+ /**
+ * Dereferences the instance (redirects the -> operator to the managed
+ * pointer).
+ */
+ NoAddRefRelease<C> *operator->() const
+ {
+ AssertMsg (p, ("Managed pointer must not be null\n"));
+ return (NoAddRefRelease <C> *) p;
+ }
+
+ template<class I>
+ HRESULT queryInterfaceTo(I **pp) const
+ {
+ if (pp)
+ {
+ if (p)
+ return p->QueryInterface(COM_IIDOF(I), (void **)pp);
+ *pp = NULL;
+ return S_OK;
+ }
+ return E_INVALIDARG;
+ }
+
+ /** Intended to pass instances as out parameters to interface methods */
+ C **asOutParam()
+ {
+ setNull();
+ return &p;
+ }
+
+private:
+
+ void addref()
+ {
+ if (p)
+ RefOps<C>::addref(p);
+ }
+
+ void release()
+ {
+ if (p)
+ RefOps<C>::release(p);
+ }
+
+ void safe_assign(C *that_p)
+ {
+ /* be aware of self-assignment */
+ if (that_p)
+ RefOps<C>::addref(that_p);
+ release();
+ p = that_p;
+ }
+
+ C *p;
+};
+
+/**
+ * Smart COM pointer wrapper that automatically manages refcounting of
+ * interface pointers.
+ *
+ * @param I COM interface class
+ */
+template<class I, template<class> class RefOps = ComStrongRef>
+class ComPtr : public ComPtrBase<I, RefOps>
+{
+ typedef ComPtrBase<I, RefOps> Base;
+
+public:
+
+ ComPtr() : Base() {}
+ ComPtr(const ComPtr &that) : Base(that) {}
+ ComPtr&operator= (const ComPtr &that)
+ {
+ Base::operator=(that);
+ return *this;
+ }
+
+ template<class OI>
+ ComPtr(OI *that_p) : Base () { operator=(that_p); }
+
+ /* specialization for I */
+ ComPtr(I *that_p) : Base (that_p) {}
+
+ template <class OC>
+ ComPtr(const ComPtr<OC, RefOps> &oc) : Base() { operator=((OC *) oc); }
+
+ template<class OI>
+ ComPtr &operator=(OI *that_p)
+ {
+ if (that_p)
+ that_p->QueryInterface(COM_IIDOF(I), (void **)Base::asOutParam());
+ else
+ Base::setNull();
+ return *this;
+ }
+
+ /* specialization for I */
+ ComPtr &operator=(I *that_p)
+ {
+ Base::operator=(that_p);
+ return *this;
+ }
+
+ template<class OC>
+ ComPtr &operator=(const ComPtr<OC, RefOps> &oc)
+ {
+ return operator=((OC *) oc);
+ }
+};
+#endif
+
+static HRESULT netIfWinFindAdapterClassById(IWbemServices *pSvc, const GUID *pGuid, IWbemClassObject **pAdapterConfig)
+{
+ HRESULT hr;
+
+ WCHAR wszGuid[50];
+ int cwcGuid = StringFromGUID2(*pGuid, wszGuid, RT_ELEMENTS(wszGuid));
+ if (cwcGuid)
+ {
+ com::BstrFmt bstrQuery("SELECT * FROM Win32_NetworkAdapterConfiguration WHERE SettingID = \"%ls\"", wszGuid);
+ IEnumWbemClassObject* pEnumerator = NULL;
+ hr = pSvc->ExecQuery(com::Bstr("WQL").raw(), bstrQuery.raw(), WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
+ NULL, &pEnumerator);
+ if (SUCCEEDED(hr))
+ {
+ if (pEnumerator)
+ {
+ IWbemClassObject *pclsObj = NULL;
+ ULONG uReturn = 0;
+ hr = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn);
+ NonStandardLogFlow(("netIfWinFindAdapterClassById: IEnumWbemClassObject::Next -> hr=0x%x pclsObj=%p uReturn=%u 42=%u\n",
+ hr, (void *)pclsObj, uReturn, 42));
+ if (SUCCEEDED(hr))
+ {
+ if (uReturn && pclsObj != NULL)
+ {
+ *pAdapterConfig = pclsObj;
+ pEnumerator->Release();
+ NonStandardLogFlow(("netIfWinFindAdapterClassById: S_OK and %p\n", *pAdapterConfig));
+ return S_OK;
+ }
+ hr = E_FAIL;
+ }
+ pEnumerator->Release();
+ }
+ else
+ {
+ NonStandardLogFlow(("ExecQuery returned no enumerator\n"));
+ hr = E_FAIL;
+ }
+ }
+ else
+ NonStandardLogFlow(("ExecQuery failed (0x%x)\n", hr));
+ }
+ else
+ {
+ DWORD winEr = GetLastError();
+ hr = HRESULT_FROM_WIN32(winEr);
+ if (SUCCEEDED(hr))
+ hr = E_FAIL;
+ NonStandardLogFlow(("StringFromGUID2 failed winEr=%u, hr=0x%x\n", winEr, hr));
+ }
+
+ NonStandardLogFlow(("netIfWinFindAdapterClassById: 0x%x and %p\n", hr, *pAdapterConfig));
+ return hr;
+}
+
+static HRESULT netIfWinIsHostOnly(IWbemClassObject *pAdapterConfig, BOOL *pfIsHostOnly)
+{
+ VARIANT vtServiceName;
+ VariantInit(&vtServiceName);
+
+ HRESULT hr = pAdapterConfig->Get(L"ServiceName", 0 /*lFlags*/, &vtServiceName, NULL /*pvtType*/, NULL /*plFlavor*/);
+ if (SUCCEEDED(hr))
+ {
+ *pfIsHostOnly = RTUtf16CmpAscii(vtServiceName.bstrVal, "VBoxNetAdp") == 0;
+
+ VariantClear(&vtServiceName);
+ }
+
+ return hr;
+}
+
+static HRESULT netIfWinGetIpSettings(IWbemClassObject * pAdapterConfig, ULONG *pIpv4, ULONG *pMaskv4)
+{
+ *pIpv4 = 0;
+ *pMaskv4 = 0;
+
+ VARIANT vtIp;
+ VariantInit(&vtIp);
+ HRESULT hr = pAdapterConfig->Get(L"IPAddress", 0, &vtIp, 0, 0);
+ if (SUCCEEDED(hr))
+ {
+ if (vtIp.vt == (VT_ARRAY | VT_BSTR))
+ {
+ VARIANT vtMask;
+ VariantInit(&vtMask);
+ hr = pAdapterConfig->Get(L"IPSubnet", 0, &vtMask, 0, 0);
+ if (SUCCEEDED(hr))
+ {
+ if (vtMask.vt == (VT_ARRAY | VT_BSTR))
+ {
+ SAFEARRAY *pIpArray = vtIp.parray;
+ SAFEARRAY *pMaskArray = vtMask.parray;
+ if (pIpArray && pMaskArray)
+ {
+ BSTR pBstrCurIp;
+ BSTR pBstrCurMask;
+ for (LONG i = 0;
+ SafeArrayGetElement(pIpArray, &i, (PVOID)&pBstrCurIp) == S_OK
+ && SafeArrayGetElement(pMaskArray, &i, (PVOID)&pBstrCurMask) == S_OK;
+ i++)
+ {
+ com::Utf8Str strIp(pBstrCurIp);
+ ULONG Ipv4 = inet_addr(strIp.c_str());
+ if (Ipv4 != INADDR_NONE)
+ {
+ *pIpv4 = Ipv4;
+
+ com::Utf8Str strMask(pBstrCurMask);
+ *pMaskv4 = inet_addr(strMask.c_str());
+ break;
+ }
+ }
+ }
+ }
+ VariantClear(&vtMask);
+ }
+ }
+ VariantClear(&vtIp);
+ }
+
+ return hr;
+}
+
+#if 0 /* unused */
+
+static HRESULT netIfWinHasIpSettings(IWbemClassObject * pAdapterConfig, SAFEARRAY * pCheckIp, SAFEARRAY * pCheckMask, bool *pFound)
+{
+ VARIANT vtIp;
+ HRESULT hr;
+ VariantInit(&vtIp);
+
+ *pFound = false;
+
+ hr = pAdapterConfig->Get(L"IPAddress", 0, &vtIp, 0, 0);
+ if (SUCCEEDED(hr))
+ {
+ VARIANT vtMask;
+ VariantInit(&vtMask);
+ hr = pAdapterConfig->Get(L"IPSubnet", 0, &vtMask, 0, 0);
+ if (SUCCEEDED(hr))
+ {
+ SAFEARRAY * pIpArray = vtIp.parray;
+ SAFEARRAY * pMaskArray = vtMask.parray;
+ if (pIpArray && pMaskArray)
+ {
+ BSTR pIp, pMask;
+ for (LONG k = 0;
+ SafeArrayGetElement(pCheckIp, &k, (PVOID)&pIp) == S_OK
+ && SafeArrayGetElement(pCheckMask, &k, (PVOID)&pMask) == S_OK;
+ k++)
+ {
+ BSTR pCurIp;
+ BSTR pCurMask;
+ for (LONG i = 0;
+ SafeArrayGetElement(pIpArray, &i, (PVOID)&pCurIp) == S_OK
+ && SafeArrayGetElement(pMaskArray, &i, (PVOID)&pCurMask) == S_OK;
+ i++)
+ {
+ if (!wcsicmp(pCurIp, pIp))
+ {
+ if (!wcsicmp(pCurMask, pMask))
+ *pFound = true;
+ break;
+ }
+ }
+ }
+ }
+
+
+ VariantClear(&vtMask);
+ }
+
+ VariantClear(&vtIp);
+ }
+
+ return hr;
+}
+
+static HRESULT netIfWinWaitIpSettings(IWbemServices *pSvc, const GUID * pGuid, SAFEARRAY * pCheckIp, SAFEARRAY * pCheckMask, ULONG sec2Wait, bool *pFound)
+{
+ /* on Vista we need to wait for the address to get applied */
+ /* wait for the address to appear in the list */
+ HRESULT hr = S_OK;
+ ULONG i;
+ *pFound = false;
+ ComPtr<IWbemClassObject> pAdapterConfig;
+ for (i = 0;
+ (hr = netIfWinFindAdapterClassById(pSvc, pGuid, pAdapterConfig.asOutParam())) == S_OK
+ && (hr = netIfWinHasIpSettings(pAdapterConfig, pCheckIp, pCheckMask, pFound)) == S_OK
+ && !(*pFound)
+ && i < sec2Wait/6;
+ i++)
+ {
+ Sleep(6000);
+ }
+
+ return hr;
+}
+
+#endif /* unused */
+
+static HRESULT netIfWinCreateIWbemServices(IWbemServices **ppSvc)
+{
+ IWbemLocator *pLoc = NULL;
+ HRESULT hr = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID *)&pLoc);
+ if (SUCCEEDED(hr))
+ {
+ IWbemServices *pSvc = NULL;
+ hr = pLoc->ConnectServer(com::Bstr(L"ROOT\\CIMV2").raw(), /* [in] const BSTR strNetworkResource */
+ NULL, /* [in] const BSTR strUser */
+ NULL, /* [in] const BSTR strPassword */
+ 0, /* [in] const BSTR strLocale */
+ NULL, /* [in] LONG lSecurityFlags */
+ 0, /* [in] const BSTR strAuthority */
+ 0, /* [in] IWbemContext* pCtx */
+ &pSvc /* [out] IWbemServices** ppNamespace */);
+ if (SUCCEEDED(hr))
+ {
+ hr = CoSetProxyBlanket(pSvc, /* IUnknown * pProxy */
+ RPC_C_AUTHN_WINNT, /* DWORD dwAuthnSvc */
+ RPC_C_AUTHZ_NONE, /* DWORD dwAuthzSvc */
+ NULL, /* WCHAR * pServerPrincName */
+ RPC_C_AUTHN_LEVEL_CALL, /* DWORD dwAuthnLevel */
+ RPC_C_IMP_LEVEL_IMPERSONATE, /* DWORD dwImpLevel */
+ NULL, /* RPC_AUTH_IDENTITY_HANDLE pAuthInfo */
+ EOAC_NONE /* DWORD dwCapabilities */
+ );
+ if (SUCCEEDED(hr))
+ {
+ *ppSvc = pSvc;
+ /* do not need it any more */
+ pLoc->Release();
+ return hr;
+ }
+
+ NonStandardLogFlow(("CoSetProxyBlanket failed: %Rhrc\n", hr));
+ pSvc->Release();
+ }
+ else
+ NonStandardLogFlow(("ConnectServer failed: %Rhrc\n", hr));
+ pLoc->Release();
+ }
+ else
+ NonStandardLogFlow(("CoCreateInstance failed: %Rhrc\n", hr));
+ return hr;
+}
+
+static HRESULT netIfWinAdapterConfigPath(IWbemClassObject *pObj, com::Bstr *pRet)
+{
+ VARIANT index;
+ VariantInit(&index);
+ HRESULT hr = pObj->Get(L"Index", 0, &index, 0, 0);
+ if (SUCCEEDED(hr))
+ hr = pRet->printfNoThrow("Win32_NetworkAdapterConfiguration.Index='%u'", index.uintVal);
+ else
+ {
+ pRet->setNull();
+ NonStandardLogFlow(("Get failed: %Rhrc\n", hr));
+ }
+ return hr;
+}
+
+static HRESULT netIfExecMethod(IWbemServices * pSvc, IWbemClassObject *pClass, com::Bstr const &rObjPath,
+ const char *pszMethodName, LPWSTR *papwszArgNames, LPVARIANT *pArgs, UINT cArgs,
+ IWbemClassObject **ppOutParams)
+{
+ *ppOutParams = NULL;
+ com::Bstr bstrMethodName;
+ HRESULT hr = bstrMethodName.assignEx(pszMethodName);
+ if (SUCCEEDED(hr))
+ {
+ ComPtr<IWbemClassObject> pInParamsDefinition;
+ ComPtr<IWbemClassObject> pClassInstance;
+ if (cArgs)
+ {
+ hr = pClass->GetMethod(bstrMethodName.raw(), 0, pInParamsDefinition.asOutParam(), NULL);
+ if (SUCCEEDED(hr))
+ {
+ hr = pInParamsDefinition->SpawnInstance(0, pClassInstance.asOutParam());
+ if (SUCCEEDED(hr))
+ {
+ for (UINT i = 0; i < cArgs; i++)
+ {
+ hr = pClassInstance->Put(papwszArgNames[i], 0, pArgs[i], 0);
+ if (FAILED(hr))
+ break;
+ }
+ }
+ }
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ IWbemClassObject *pOutParams = NULL;
+ hr = pSvc->ExecMethod(rObjPath.raw(), bstrMethodName.raw(), 0, NULL, pClassInstance, &pOutParams, NULL);
+ if (SUCCEEDED(hr))
+ *ppOutParams = pOutParams;
+ }
+ }
+
+ return hr;
+}
+
+static HRESULT netIfWinCreateIpArray(SAFEARRAY **ppArray, in_addr const *paIps, UINT cIps)
+{
+ HRESULT hr = S_OK;
+ SAFEARRAY *pIpArray = SafeArrayCreateVector(VT_BSTR, 0, cIps);
+ if (pIpArray)
+ {
+ for (UINT i = 0; i < cIps; i++)
+ {
+ com::Bstr bstrVal;
+ hr = bstrVal.printfNoThrow("%RTnaipv4", paIps[i].s_addr);
+ if (SUCCEEDED(hr))
+ {
+ Assert(bstrVal.equals(inet_ntoa(paIps[i])));
+
+ BSTR pRawVal;
+ hr = bstrVal.detachToEx(&pRawVal);
+ if (SUCCEEDED(hr))
+ {
+ LONG aIndex[1] = { (LONG)i };
+ hr = SafeArrayPutElement(pIpArray, aIndex, pRawVal);
+ if (SUCCEEDED(hr))
+ continue;
+ SysFreeString(pRawVal);
+ }
+ }
+ break;
+ }
+
+ if (SUCCEEDED(hr))
+ *ppArray = pIpArray;
+ else
+ SafeArrayDestroy(pIpArray);
+ }
+ else
+ hr = HRESULT_FROM_WIN32(GetLastError());
+ return hr;
+}
+
+#if 0 /* unused */
+static HRESULT netIfWinCreateIpArrayV4V6(SAFEARRAY **ppArray, BSTR Ip)
+{
+ HRESULT hr;
+ SAFEARRAY *pIpArray = SafeArrayCreateVector(VT_BSTR, 0, 1);
+ if (pIpArray)
+ {
+ BSTR val = com::Bstr(Ip, false).copy();
+ long aIndex[1];
+ aIndex[0] = 0;
+ hr = SafeArrayPutElement(pIpArray, aIndex, val);
+ if (FAILED(hr))
+ {
+ SysFreeString(val);
+ SafeArrayDestroy(pIpArray);
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ *ppArray = pIpArray;
+ }
+ }
+ else
+ hr = HRESULT_FROM_WIN32(GetLastError());
+
+ return hr;
+}
+#endif
+
+
+static HRESULT netIfWinCreateIpArrayVariantV4(VARIANT *pIpAddresses, in_addr const *paIps, UINT cIps)
+{
+ VariantInit(pIpAddresses);
+ pIpAddresses->vt = VT_ARRAY | VT_BSTR;
+
+ SAFEARRAY *pIpArray;
+ HRESULT hr = netIfWinCreateIpArray(&pIpArray, paIps, cIps);
+ if (SUCCEEDED(hr))
+ pIpAddresses->parray = pIpArray;
+ return hr;
+}
+
+#if 0 /* unused */
+static HRESULT netIfWinCreateIpArrayVariantV4V6(VARIANT * pIpAddresses, BSTR Ip)
+{
+ HRESULT hr;
+ VariantInit(pIpAddresses);
+ pIpAddresses->vt = VT_ARRAY | VT_BSTR;
+ SAFEARRAY *pIpArray;
+ hr = netIfWinCreateIpArrayV4V6(&pIpArray, Ip);
+ if (SUCCEEDED(hr))
+ {
+ pIpAddresses->parray = pIpArray;
+ }
+ return hr;
+}
+#endif
+
+static HRESULT netIfWinEnableStatic(IWbemServices *pSvc, const GUID *pGuid, com::Bstr &rObjPath, VARIANT *pIp, VARIANT *pMask)
+{
+ com::Bstr bstrClassName;
+ HRESULT hr = bstrClassName.assignEx("Win32_NetworkAdapterConfiguration");
+ if (SUCCEEDED(hr))
+ {
+ ComPtr<IWbemClassObject> pClass;
+ hr = pSvc->GetObject(bstrClassName.raw(), 0, NULL, pClass.asOutParam(), NULL);
+ if (SUCCEEDED(hr))
+ {
+ LPWSTR argNames[] = {L"IPAddress", L"SubnetMask"};
+ LPVARIANT args[] = { pIp, pMask };
+
+ ComPtr<IWbemClassObject> pOutParams;
+ hr = netIfExecMethod(pSvc, pClass, rObjPath.raw(), "EnableStatic", argNames, args,
+ 2, pOutParams.asOutParam());
+ if (SUCCEEDED(hr))
+ {
+ com::Bstr bstrReturnValue;
+ hr = bstrReturnValue.assignEx("ReturnValue");
+ if (SUCCEEDED(hr))
+ {
+ VARIANT varReturnValue;
+ VariantInit(&varReturnValue);
+ hr = pOutParams->Get(bstrReturnValue.raw(), 0, &varReturnValue, NULL, 0);
+ Assert(SUCCEEDED(hr));
+ if (SUCCEEDED(hr))
+ {
+ //Assert(varReturnValue.vt == VT_UINT);
+ int winEr = varReturnValue.uintVal;
+ switch (winEr)
+ {
+ case 0:
+ {
+ hr = S_OK;
+ //bool bFound;
+ //HRESULT tmpHr = netIfWinWaitIpSettings(pSvc, pGuid, pIp->parray, pMask->parray, 180, &bFound);
+ NOREF(pGuid);
+ break;
+ }
+ default:
+ hr = HRESULT_FROM_WIN32( winEr );
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ return hr;
+}
+
+
+static HRESULT netIfWinEnableStaticV4(IWbemServices *pSvc, const GUID *pGuid, com::Bstr &rObjPath,
+ in_addr const *paIps, in_addr const *paMasks, UINT cIpAndMasks)
+{
+ VARIANT ipAddresses;
+ HRESULT hr = netIfWinCreateIpArrayVariantV4(&ipAddresses, paIps, cIpAndMasks);
+ if (SUCCEEDED(hr))
+ {
+ VARIANT ipMasks;
+ hr = netIfWinCreateIpArrayVariantV4(&ipMasks, paMasks, cIpAndMasks);
+ if (SUCCEEDED(hr))
+ {
+ hr = netIfWinEnableStatic(pSvc, pGuid, rObjPath, &ipAddresses, &ipMasks);
+ VariantClear(&ipMasks);
+ }
+ VariantClear(&ipAddresses);
+ }
+ return hr;
+}
+
+#if 0 /* unused */
+
+static HRESULT netIfWinEnableStaticV4V6(IWbemServices * pSvc, const GUID * pGuid, BSTR ObjPath, BSTR Ip, BSTR Mask)
+{
+ VARIANT ipAddresses;
+ HRESULT hr = netIfWinCreateIpArrayVariantV4V6(&ipAddresses, Ip);
+ if (SUCCEEDED(hr))
+ {
+ VARIANT ipMasks;
+ hr = netIfWinCreateIpArrayVariantV4V6(&ipMasks, Mask);
+ if (SUCCEEDED(hr))
+ {
+ hr = netIfWinEnableStatic(pSvc, pGuid, ObjPath, &ipAddresses, &ipMasks);
+ VariantClear(&ipMasks);
+ }
+ VariantClear(&ipAddresses);
+ }
+ return hr;
+}
+
+/* win API allows to set gw metrics as well, we are not setting them */
+static HRESULT netIfWinSetGateways(IWbemServices * pSvc, BSTR ObjPath, VARIANT * pGw)
+{
+ ComPtr<IWbemClassObject> pClass;
+ BSTR ClassName = SysAllocString(L"Win32_NetworkAdapterConfiguration");
+ HRESULT hr;
+ if (ClassName)
+ {
+ hr = pSvc->GetObject(ClassName, 0, NULL, pClass.asOutParam(), NULL);
+ if (SUCCEEDED(hr))
+ {
+ LPWSTR argNames[] = {L"DefaultIPGateway"};
+ LPVARIANT args[] = {pGw};
+ ComPtr<IWbemClassObject> pOutParams;
+
+ hr = netIfExecMethod(pSvc, pClass, ObjPath, com::Bstr(L"SetGateways"), argNames, args, 1, pOutParams.asOutParam());
+ if (SUCCEEDED(hr))
+ {
+ VARIANT varReturnValue;
+ hr = pOutParams->Get(com::Bstr(L"ReturnValue"), 0, &varReturnValue, NULL, 0);
+ Assert(SUCCEEDED(hr));
+ if (SUCCEEDED(hr))
+ {
+// Assert(varReturnValue.vt == VT_UINT);
+ int winEr = varReturnValue.uintVal;
+ switch (winEr)
+ {
+ case 0:
+ hr = S_OK;
+ break;
+ default:
+ hr = HRESULT_FROM_WIN32( winEr );
+ break;
+ }
+ }
+ }
+ }
+ SysFreeString(ClassName);
+ }
+ else
+ hr = HRESULT_FROM_WIN32(GetLastError());
+
+ return hr;
+}
+
+/* win API allows to set gw metrics as well, we are not setting them */
+static HRESULT netIfWinSetGatewaysV4(IWbemServices * pSvc, BSTR ObjPath, in_addr* aGw, UINT cGw)
+{
+ VARIANT gwais;
+ HRESULT hr = netIfWinCreateIpArrayVariantV4(&gwais, aGw, cGw);
+ if (SUCCEEDED(hr))
+ {
+ netIfWinSetGateways(pSvc, ObjPath, &gwais);
+ VariantClear(&gwais);
+ }
+ return hr;
+}
+
+/* win API allows to set gw metrics as well, we are not setting them */
+static HRESULT netIfWinSetGatewaysV4V6(IWbemServices * pSvc, BSTR ObjPath, BSTR Gw)
+{
+ VARIANT vGw;
+ HRESULT hr = netIfWinCreateIpArrayVariantV4V6(&vGw, Gw);
+ if (SUCCEEDED(hr))
+ {
+ netIfWinSetGateways(pSvc, ObjPath, &vGw);
+ VariantClear(&vGw);
+ }
+ return hr;
+}
+
+#endif /* unused */
+
+static HRESULT netIfWinEnableDHCP(IWbemServices * pSvc, const com::Bstr &rObjPath)
+{
+ com::Bstr bstrClassName;
+ HRESULT hr = bstrClassName.assignEx("Win32_NetworkAdapterConfiguration");
+ if (SUCCEEDED(hr))
+ {
+ ComPtr<IWbemClassObject> pClass;
+ hr = pSvc->GetObject(bstrClassName.raw(), 0, NULL, pClass.asOutParam(), NULL);
+ if (SUCCEEDED(hr))
+ {
+ ComPtr<IWbemClassObject> pOutParams;
+ hr = netIfExecMethod(pSvc, pClass, rObjPath, "EnableDHCP", NULL, NULL, 0, pOutParams.asOutParam());
+ if (SUCCEEDED(hr))
+ {
+ com::Bstr bstrReturnValue;
+ hr = bstrReturnValue.assignEx("ReturnValue");
+ if (SUCCEEDED(hr))
+ {
+ VARIANT varReturnValue;
+ VariantInit(&varReturnValue);
+ hr = pOutParams->Get(bstrReturnValue.raw(), 0, &varReturnValue, NULL, 0);
+ Assert(SUCCEEDED(hr));
+ if (SUCCEEDED(hr))
+ {
+ //Assert(varReturnValue.vt == VT_UINT);
+ int winEr = varReturnValue.uintVal;
+ switch (winEr)
+ {
+ case 0:
+ hr = S_OK;
+ break;
+ default:
+ hr = HRESULT_FROM_WIN32( winEr );
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ return hr;
+}
+
+static HRESULT netIfWinDhcpRediscover(IWbemServices * pSvc, const com::Bstr &rObjPath)
+{
+ com::Bstr bstrClassName;
+ HRESULT hr = bstrClassName.assignEx("Win32_NetworkAdapterConfiguration");
+ if (SUCCEEDED(hr))
+ {
+ ComPtr<IWbemClassObject> pClass;
+ hr = pSvc->GetObject(bstrClassName.raw(), 0, NULL, pClass.asOutParam(), NULL);
+ if (SUCCEEDED(hr))
+ {
+ ComPtr<IWbemClassObject> pOutParams;
+ hr = netIfExecMethod(pSvc, pClass, rObjPath, "ReleaseDHCPLease", NULL, NULL, 0, pOutParams.asOutParam());
+ if (SUCCEEDED(hr))
+ {
+ com::Bstr bstrReturnValue;
+ hr = bstrReturnValue.assignEx("ReturnValue");
+ if (SUCCEEDED(hr))
+ {
+ VARIANT varReturnValue;
+ VariantInit(&varReturnValue);
+ hr = pOutParams->Get(bstrReturnValue.raw(), 0, &varReturnValue, NULL, 0);
+ Assert(SUCCEEDED(hr));
+ if (SUCCEEDED(hr))
+ {
+ //Assert(varReturnValue.vt == VT_UINT);
+ int winEr = varReturnValue.uintVal;
+ if (winEr == 0)
+ {
+ hr = netIfExecMethod(pSvc, pClass, rObjPath, "RenewDHCPLease", NULL, NULL, 0, pOutParams.asOutParam());
+ if (SUCCEEDED(hr))
+ {
+ hr = pOutParams->Get(bstrReturnValue.raw(), 0, &varReturnValue, NULL, 0);
+ Assert(SUCCEEDED(hr));
+ if (SUCCEEDED(hr))
+ {
+ //Assert(varReturnValue.vt == VT_UINT);
+ winEr = varReturnValue.uintVal;
+ if (winEr == 0)
+ hr = S_OK;
+ else
+ hr = HRESULT_FROM_WIN32( winEr );
+ }
+ }
+ }
+ else
+ hr = HRESULT_FROM_WIN32( winEr );
+ }
+ }
+ }
+ }
+ }
+
+ return hr;
+}
+
+static HRESULT vboxNetCfgWinIsDhcpEnabled(IWbemClassObject *pAdapterConfig, BOOL *pfEnabled)
+{
+ VARIANT vtEnabled;
+ VariantInit(&vtEnabled);
+ HRESULT hr = pAdapterConfig->Get(L"DHCPEnabled", 0, &vtEnabled, 0, 0);
+ if (SUCCEEDED(hr))
+ *pfEnabled = vtEnabled.boolVal;
+ else
+ *pfEnabled = FALSE;
+ return hr;
+}
+
+VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinGetAdapterSettings(IN const GUID *pGuid, OUT PADAPTER_SETTINGS pSettings)
+{
+ ComPtr<IWbemServices> pSvc;
+ HRESULT hr = netIfWinCreateIWbemServices(pSvc.asOutParam());
+ if (SUCCEEDED(hr))
+ {
+ ComPtr<IWbemClassObject> pAdapterConfig;
+ hr = netIfWinFindAdapterClassById(pSvc, pGuid, pAdapterConfig.asOutParam());
+ if (SUCCEEDED(hr))
+ {
+ hr = vboxNetCfgWinIsDhcpEnabled(pAdapterConfig, &pSettings->bDhcp);
+ if (SUCCEEDED(hr))
+ hr = netIfWinGetIpSettings(pAdapterConfig, &pSettings->ip, &pSettings->mask);
+ }
+ }
+
+ return hr;
+}
+
+VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinIsDhcpEnabled(const GUID * pGuid, BOOL *pEnabled)
+{
+ ComPtr<IWbemServices> pSvc;
+ HRESULT hr = netIfWinCreateIWbemServices(pSvc.asOutParam());
+ if (SUCCEEDED(hr))
+ {
+ ComPtr<IWbemClassObject> pAdapterConfig;
+ hr = netIfWinFindAdapterClassById(pSvc, pGuid, pAdapterConfig.asOutParam());
+ if (SUCCEEDED(hr))
+ {
+ VARIANT vtEnabled;
+ hr = pAdapterConfig->Get(L"DHCPEnabled", 0, &vtEnabled, 0, 0);
+ if (SUCCEEDED(hr))
+ *pEnabled = vtEnabled.boolVal;
+ }
+ }
+
+ return hr;
+}
+
+VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinEnableStaticIpConfig(IN const GUID *pGuid, IN ULONG ip, IN ULONG mask)
+{
+ NonStandardLogFlow(("VBoxNetCfgWinEnableStaticIpConfig: ip=0x%x mask=0x%x\n", ip, mask));
+ ComPtr<IWbemServices> pSvc;
+ HRESULT hr = netIfWinCreateIWbemServices(pSvc.asOutParam());
+ if (SUCCEEDED(hr))
+ {
+ ComPtr<IWbemClassObject> pAdapterConfig;
+ hr = netIfWinFindAdapterClassById(pSvc, pGuid, pAdapterConfig.asOutParam());
+ if (SUCCEEDED(hr))
+ {
+ BOOL fIsHostOnly;
+ hr = netIfWinIsHostOnly(pAdapterConfig, &fIsHostOnly);
+ if (SUCCEEDED(hr))
+ {
+ if (fIsHostOnly)
+ {
+ in_addr aIp[1];
+ in_addr aMask[1];
+ aIp[0].S_un.S_addr = ip;
+ aMask[0].S_un.S_addr = mask;
+
+ com::Bstr bstrObjPath;
+ hr = netIfWinAdapterConfigPath(pAdapterConfig, &bstrObjPath);
+ if (SUCCEEDED(hr))
+ {
+ hr = netIfWinEnableStaticV4(pSvc, pGuid, bstrObjPath, aIp, aMask, ip != 0 ? 1 : 0);
+ if (SUCCEEDED(hr))
+ {
+#if 0
+ in_addr aGw[1];
+ aGw[0].S_un.S_addr = gw;
+ hr = netIfWinSetGatewaysV4(pSvc, bstrObjPath, aGw, 1);
+ if (SUCCEEDED(hr))
+#endif
+ {
+ }
+ }
+ }
+ }
+ else
+ {
+ hr = E_FAIL;
+ }
+ }
+ }
+ }
+
+ NonStandardLogFlow(("VBoxNetCfgWinEnableStaticIpConfig: returns %Rhrc\n", hr));
+ return hr;
+}
+
+#if 0
+static HRESULT netIfEnableStaticIpConfigV6(const GUID *pGuid, IN_BSTR aIPV6Address, IN_BSTR aIPV6Mask, IN_BSTR aIPV6DefaultGateway)
+{
+ HRESULT hr;
+ ComPtr<IWbemServices> pSvc;
+ hr = netIfWinCreateIWbemServices(pSvc.asOutParam());
+ if (SUCCEEDED(hr))
+ {
+ ComPtr<IWbemClassObject> pAdapterConfig;
+ hr = netIfWinFindAdapterClassById(pSvc, pGuid, pAdapterConfig.asOutParam());
+ if (SUCCEEDED(hr))
+ {
+ BSTR ObjPath;
+ hr = netIfWinAdapterConfigPath(pAdapterConfig, &ObjPath);
+ if (SUCCEEDED(hr))
+ {
+ hr = netIfWinEnableStaticV4V6(pSvc, pAdapterConfig, ObjPath, aIPV6Address, aIPV6Mask);
+ if (SUCCEEDED(hr))
+ {
+ if (aIPV6DefaultGateway)
+ {
+ hr = netIfWinSetGatewaysV4V6(pSvc, ObjPath, aIPV6DefaultGateway);
+ }
+ if (SUCCEEDED(hr))
+ {
+// hr = netIfWinUpdateConfig(pIf);
+ }
+ }
+ SysFreeString(ObjPath);
+ }
+ }
+ }
+
+ return SUCCEEDED(hr) ? VINF_SUCCESS : VERR_GENERAL_FAILURE;
+}
+
+static HRESULT netIfEnableStaticIpConfigV6(const GUID *pGuid, IN_BSTR aIPV6Address, ULONG aIPV6MaskPrefixLength)
+{
+ RTNETADDRIPV6 Mask;
+ int rc = RTNetPrefixToMaskIPv6(aIPV6MaskPrefixLength, &Mask);
+ if (RT_SUCCESS(rc))
+ {
+ Bstr maskStr = composeIPv6Address(&Mask);
+ rc = netIfEnableStaticIpConfigV6(pGuid, aIPV6Address, maskStr, NULL);
+ }
+ return rc;
+}
+#endif
+
+VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinEnableDynamicIpConfig(IN const GUID *pGuid)
+{
+ ComPtr<IWbemServices> pSvc;
+ HRESULT hr = netIfWinCreateIWbemServices(pSvc.asOutParam());
+ if (SUCCEEDED(hr))
+ {
+ ComPtr<IWbemClassObject> pAdapterConfig;
+ hr = netIfWinFindAdapterClassById(pSvc, pGuid, pAdapterConfig.asOutParam());
+ if (SUCCEEDED(hr))
+ {
+ BOOL fIsHostOnly;
+ hr = netIfWinIsHostOnly(pAdapterConfig, &fIsHostOnly);
+ if (SUCCEEDED(hr))
+ {
+ if (fIsHostOnly)
+ {
+ com::Bstr bstrObjPath;
+ hr = netIfWinAdapterConfigPath(pAdapterConfig, &bstrObjPath);
+ if (SUCCEEDED(hr))
+ {
+ hr = netIfWinEnableDHCP(pSvc, bstrObjPath);
+ if (SUCCEEDED(hr))
+ {
+ //hr = netIfWinUpdateConfig(pIf);
+ }
+ }
+ }
+ else
+ hr = E_FAIL;
+ }
+ }
+ }
+ return hr;
+}
+
+VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinDhcpRediscover(IN const GUID *pGuid)
+{
+ ComPtr<IWbemServices> pSvc;
+ HRESULT hr = netIfWinCreateIWbemServices(pSvc.asOutParam());
+ if (SUCCEEDED(hr))
+ {
+ ComPtr<IWbemClassObject> pAdapterConfig;
+ hr = netIfWinFindAdapterClassById(pSvc, pGuid, pAdapterConfig.asOutParam());
+ if (SUCCEEDED(hr))
+ {
+ BOOL fIsHostOnly;
+ hr = netIfWinIsHostOnly(pAdapterConfig, &fIsHostOnly);
+ if (SUCCEEDED(hr))
+ {
+ if (fIsHostOnly)
+ {
+ com::Bstr bstrObjPath;
+ hr = netIfWinAdapterConfigPath(pAdapterConfig, &bstrObjPath);
+ if (SUCCEEDED(hr))
+ {
+ hr = netIfWinDhcpRediscover(pSvc, bstrObjPath);
+ if (SUCCEEDED(hr))
+ {
+ //hr = netIfWinUpdateConfig(pIf);
+ }
+ }
+ }
+ else
+ hr = E_FAIL;
+ }
+ }
+ }
+
+
+ return hr;
+}
+
+static const char *vboxNetCfgWinAddrToStr(char *pszBuf, size_t cbBuf, LPSOCKADDR pAddr)
+{
+ switch (pAddr->sa_family)
+ {
+ case AF_INET:
+ RTStrPrintf(pszBuf, cbBuf, "%d.%d.%d.%d",
+ ((PSOCKADDR_IN)pAddr)->sin_addr.S_un.S_un_b.s_b1,
+ ((PSOCKADDR_IN)pAddr)->sin_addr.S_un.S_un_b.s_b2,
+ ((PSOCKADDR_IN)pAddr)->sin_addr.S_un.S_un_b.s_b3,
+ ((PSOCKADDR_IN)pAddr)->sin_addr.S_un.S_un_b.s_b4);
+ break;
+ case AF_INET6:
+ RTStrPrintf(pszBuf, cbBuf, "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x",
+ ((PSOCKADDR_IN6)pAddr)->sin6_addr.s6_addr[0], ((PSOCKADDR_IN6)pAddr)->sin6_addr.s6_addr[1],
+ ((PSOCKADDR_IN6)pAddr)->sin6_addr.s6_addr[2], ((PSOCKADDR_IN6)pAddr)->sin6_addr.s6_addr[3],
+ ((PSOCKADDR_IN6)pAddr)->sin6_addr.s6_addr[4], ((PSOCKADDR_IN6)pAddr)->sin6_addr.s6_addr[5],
+ ((PSOCKADDR_IN6)pAddr)->sin6_addr.s6_addr[6], ((PSOCKADDR_IN6)pAddr)->sin6_addr.s6_addr[7],
+ ((PSOCKADDR_IN6)pAddr)->sin6_addr.s6_addr[8], ((PSOCKADDR_IN6)pAddr)->sin6_addr.s6_addr[9],
+ ((PSOCKADDR_IN6)pAddr)->sin6_addr.s6_addr[10], ((PSOCKADDR_IN6)pAddr)->sin6_addr.s6_addr[11],
+ ((PSOCKADDR_IN6)pAddr)->sin6_addr.s6_addr[12], ((PSOCKADDR_IN6)pAddr)->sin6_addr.s6_addr[13],
+ ((PSOCKADDR_IN6)pAddr)->sin6_addr.s6_addr[14], ((PSOCKADDR_IN6)pAddr)->sin6_addr.s6_addr[15]);
+ break;
+ default:
+ RTStrCopy(pszBuf, cbBuf, "unknown");
+ break;
+ }
+ return pszBuf;
+}
+
+typedef bool (*PFNVBOXNETCFG_IPSETTINGS_CALLBACK) (ULONG ip, ULONG mask, PVOID pContext);
+
+static void vboxNetCfgWinEnumIpConfig(PIP_ADAPTER_ADDRESSES pAddresses, PFNVBOXNETCFG_IPSETTINGS_CALLBACK pfnCallback, PVOID pContext)
+{
+ PIP_ADAPTER_ADDRESSES pAdapter;
+ for (pAdapter = pAddresses; pAdapter; pAdapter = pAdapter->Next)
+ {
+ NonStandardLogFlow(("+- Enumerating adapter '%ls' %s\n", pAdapter->FriendlyName, pAdapter->AdapterName));
+ for (PIP_ADAPTER_PREFIX pPrefix = pAdapter->FirstPrefix; pPrefix; pPrefix = pPrefix->Next)
+ {
+ char szBuf[80];
+ const char *pcszAddress = vboxNetCfgWinAddrToStr(szBuf, sizeof(szBuf), pPrefix->Address.lpSockaddr);
+
+ /* We are concerned with IPv4 only, ignore the rest. */
+ if (pPrefix->Address.lpSockaddr->sa_family != AF_INET)
+ {
+ NonStandardLogFlow(("| +- %s %d: not IPv4, ignoring\n", pcszAddress, pPrefix->PrefixLength));
+ continue;
+ }
+
+ /* Ignore invalid prefixes as well as host addresses. */
+ if (pPrefix->PrefixLength < 1 || pPrefix->PrefixLength > 31)
+ {
+ NonStandardLogFlow(("| +- %s %d: host or broadcast, ignoring\n", pcszAddress, pPrefix->PrefixLength));
+ continue;
+ }
+
+ /* Ignore multicast and beyond. */
+ ULONG ip = ((struct sockaddr_in *)pPrefix->Address.lpSockaddr)->sin_addr.s_addr;
+ if ((ip & 0xF0) > 224)
+ {
+ NonStandardLogFlow(("| +- %s %d: multicast, ignoring\n", pcszAddress, pPrefix->PrefixLength));
+ continue;
+ }
+
+ ULONG mask = htonl((~(((ULONG)~0) >> pPrefix->PrefixLength)));
+ bool fContinue = pfnCallback(ip, mask, pContext);
+ if (!fContinue)
+ {
+ NonStandardLogFlow(("| +- %s %d: CONFLICT!\n", pcszAddress, pPrefix->PrefixLength));
+ return;
+ }
+
+ NonStandardLogFlow(("| +- %s %d: no conflict, moving on\n", pcszAddress, pPrefix->PrefixLength));
+ }
+ }
+}
+
+typedef struct _IPPROBE_CONTEXT
+{
+ ULONG Prefix;
+ bool fConflict;
+}IPPROBE_CONTEXT, *PIPPROBE_CONTEXT;
+
+#define IPPROBE_INIT(a_pContext, a_addr) \
+ do { (a_pContext)->fConflict = false; (a_pContext)->Prefix = (a_addr); } while (0)
+
+#define IPPROBE_INIT_STR(a_pContext, a_straddr) \
+ IPROBE_INIT(a_pContext, inet_addr(_straddr))
+
+static bool vboxNetCfgWinIpProbeCallback (ULONG ip, ULONG mask, PVOID pContext)
+{
+ PIPPROBE_CONTEXT pProbe = (PIPPROBE_CONTEXT)pContext;
+
+ if ((ip & mask) == (pProbe->Prefix & mask))
+ {
+ pProbe->fConflict = true;
+ return false;
+ }
+
+ return true;
+}
+
+VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinGenHostOnlyNetworkNetworkIp(OUT PULONG pNetIp, OUT PULONG pNetMask)
+{
+ HRESULT hr = S_OK;
+
+ *pNetIp = 0;
+ *pNetMask = 0;
+
+ /*
+ * MSDN recommends to pre-allocate a 15KB buffer.
+ */
+ ULONG cbBuf = 15 * _1K;
+ PIP_ADAPTER_ADDRESSES paAddresses = (PIP_ADAPTER_ADDRESSES)RTMemAllocZ(cbBuf);
+ if (!paAddresses)
+ return E_OUTOFMEMORY;
+ DWORD dwRc = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, NULL, paAddresses, &cbBuf);
+ if (dwRc == ERROR_BUFFER_OVERFLOW)
+ {
+ /* Impressive! More than 10 adapters! Get more memory and try again. */
+ RTMemFree(paAddresses);
+ paAddresses = (PIP_ADAPTER_ADDRESSES)RTMemAllocZ(cbBuf);
+ if (!paAddresses)
+ return E_OUTOFMEMORY;
+ dwRc = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, NULL, paAddresses, &cbBuf);
+ }
+ if (dwRc == NO_ERROR)
+ {
+ const ULONG ip192168 = inet_addr("192.168.0.0");
+ for (int i = 0; i < 384; i++)
+ {
+#if 0
+ ULONG ipProbe = rand()*255 / RAND_MAX;
+#else
+ uint32_t ipProbe = RTRandU32Ex(0, 255);
+#endif
+ ipProbe = ip192168 | (ipProbe << 16);
+ NonStandardLogFlow(("probing %RTnaipv4\n", ipProbe));
+
+ IPPROBE_CONTEXT Context;
+ IPPROBE_INIT(&Context, ipProbe);
+ vboxNetCfgWinEnumIpConfig(paAddresses, vboxNetCfgWinIpProbeCallback, &Context);
+ if (!Context.fConflict)
+ {
+ NonStandardLogFlow(("found unused net %RTnaipv4\n", ipProbe));
+ *pNetIp = ipProbe;
+ *pNetMask = inet_addr("255.255.255.0");
+ break;
+ }
+ }
+ if (*pNetIp == 0)
+ dwRc = ERROR_DHCP_ADDRESS_CONFLICT;
+ }
+ else
+ NonStandardLogFlow(("GetAdaptersAddresses err (%u)\n", dwRc));
+
+ RTMemFree(paAddresses);
+
+ if (dwRc != NO_ERROR)
+ hr = HRESULT_FROM_WIN32(dwRc);
+ return hr;
+}
+
+/*
+ * convenience functions to perform netflt/adp manipulations
+ */
+#define VBOXNETCFGWIN_NETFLT_ID L"sun_VBoxNetFlt"
+#define VBOXNETCFGWIN_NETFLT_MP_ID L"sun_VBoxNetFltmp"
+
+static HRESULT vboxNetCfgWinNetFltUninstall(IN INetCfg *pNc, DWORD InfRmFlags)
+{
+ INetCfgComponent *pNcc = NULL;
+ HRESULT hr = pNc->FindComponent(VBOXNETCFGWIN_NETFLT_ID, &pNcc);
+ if (hr == S_OK)
+ {
+ NonStandardLog("NetFlt is installed currently, uninstalling ...\n");
+
+ hr = VBoxNetCfgWinUninstallComponent(pNc, pNcc);
+ NonStandardLogFlow(("NetFlt component uninstallation ended with hr (%Rhrc)\n", hr));
+
+ pNcc->Release();
+ }
+ else if (hr == S_FALSE)
+ NonStandardLog("NetFlt is not installed currently\n");
+ else
+ NonStandardLogFlow(("FindComponent failed: %Rhrc\n", hr));
+
+ VBoxDrvCfgInfUninstallAllF(L"NetService", VBOXNETCFGWIN_NETFLT_ID, InfRmFlags);
+ VBoxDrvCfgInfUninstallAllF(L"Net", VBOXNETCFGWIN_NETFLT_MP_ID, InfRmFlags);
+
+ return hr;
+}
+
+VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinNetFltUninstall(IN INetCfg *pNc)
+{
+ return vboxNetCfgWinNetFltUninstall(pNc, 0);
+}
+
+VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinNetFltInstall(IN INetCfg *pNc, IN LPCWSTR const *pwszInfFullPath, IN UINT cInfFullPaths)
+{
+ HRESULT hr = vboxNetCfgWinNetFltUninstall(pNc, SUOI_FORCEDELETE);
+ if (SUCCEEDED(hr))
+ {
+ NonStandardLog("NetFlt will be installed ...\n");
+ hr = vboxNetCfgWinInstallInfAndComponent(pNc, VBOXNETCFGWIN_NETFLT_ID,
+ &GUID_DEVCLASS_NETSERVICE,
+ pwszInfFullPath,
+ cInfFullPaths,
+ NULL);
+ }
+ return hr;
+}
+
+static HRESULT vboxNetCfgWinNetAdpUninstall(IN INetCfg *pNc, LPCWSTR pwszId, DWORD InfRmFlags)
+{
+ NOREF(pNc);
+ NonStandardLog("Finding NetAdp driver package and trying to uninstall it ...\n");
+
+ VBoxDrvCfgInfUninstallAllF(L"Net", pwszId, InfRmFlags);
+ NonStandardLog("NetAdp is not installed currently\n");
+ return S_OK;
+}
+
+VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinNetAdpUninstall(IN INetCfg *pNc, IN LPCWSTR pwszId)
+{
+ return vboxNetCfgWinNetAdpUninstall(pNc, pwszId, SUOI_FORCEDELETE);
+}
+
+VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinNetAdpInstall(IN INetCfg *pNc,
+ IN LPCWSTR const pwszInfFullPath)
+{
+ NonStandardLog("NetAdp will be installed ...\n");
+ HRESULT hr = vboxNetCfgWinInstallInfAndComponent(pNc, VBOXNETCFGWIN_NETADP_ID_WSZ,
+ &GUID_DEVCLASS_NET,
+ &pwszInfFullPath,
+ 1,
+ NULL);
+ return hr;
+}
+
+
+static HRESULT vboxNetCfgWinNetLwfUninstall(IN INetCfg *pNc, DWORD InfRmFlags)
+{
+ INetCfgComponent * pNcc = NULL;
+ HRESULT hr = pNc->FindComponent(VBOXNETCFGWIN_NETLWF_ID, &pNcc);
+ if (hr == S_OK)
+ {
+ NonStandardLog("NetLwf is installed currently, uninstalling ...\n");
+
+ hr = VBoxNetCfgWinUninstallComponent(pNc, pNcc);
+
+ pNcc->Release();
+ }
+ else if (hr == S_FALSE)
+ {
+ NonStandardLog("NetLwf is not installed currently\n");
+ hr = S_OK;
+ }
+ else
+ {
+ NonStandardLogFlow(("FindComponent failed: %Rhrc\n", hr));
+ hr = S_OK;
+ }
+
+ VBoxDrvCfgInfUninstallAllF(L"NetService", VBOXNETCFGWIN_NETLWF_ID, InfRmFlags);
+
+ return hr;
+}
+
+VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinNetLwfUninstall(IN INetCfg *pNc)
+{
+ return vboxNetCfgWinNetLwfUninstall(pNc, 0);
+}
+
+static void VBoxNetCfgWinFilterLimitWorkaround(void)
+{
+ /*
+ * Need to check if the system has a limit of installed filter drivers. If it
+ * has, bump the limit to 14, which the maximum value supported by Windows 7.
+ * Note that we only touch the limit if it is set to the default value (8).
+ * See @bugref{7899}.
+ */
+ /** @todo r=bird: This code was mixing HRESULT and LSTATUS, checking the return
+ * codes using SUCCEEDED(lrc) instead of lrc == ERROR_SUCCESS. So, it might
+ * have misbehaved on bogus registry content, but worked fine on sane values. */
+ HKEY hKeyNet = NULL;
+ LSTATUS lrc = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Network", 0,
+ KEY_QUERY_VALUE | KEY_SET_VALUE, &hKeyNet);
+ if (lrc == ERROR_SUCCESS)
+ {
+ DWORD dwMaxNumFilters = 0;
+ DWORD cbMaxNumFilters = sizeof(dwMaxNumFilters);
+ lrc = RegQueryValueExW(hKeyNet, L"MaxNumFilters", NULL, NULL, (LPBYTE)&dwMaxNumFilters, &cbMaxNumFilters);
+ if (lrc == ERROR_SUCCESS && cbMaxNumFilters == sizeof(dwMaxNumFilters) && dwMaxNumFilters == 8)
+ {
+ dwMaxNumFilters = 14;
+ lrc = RegSetValueExW(hKeyNet, L"MaxNumFilters", 0, REG_DWORD, (LPBYTE)&dwMaxNumFilters, sizeof(dwMaxNumFilters));
+ if (lrc == ERROR_SUCCESS)
+ NonStandardLog("Adjusted the installed filter limit to 14...\n");
+ else
+ NonStandardLog("Failed to set MaxNumFilters, error code %d\n", lrc);
+ }
+ RegCloseKey(hKeyNet);
+ }
+ else
+ NonStandardLog("Failed to open network key, error code %d\n", lrc);
+
+}
+
+VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinNetLwfInstall(IN INetCfg *pNc, IN LPCWSTR const pwszInfFullPath)
+{
+ HRESULT hr = vboxNetCfgWinNetLwfUninstall(pNc, SUOI_FORCEDELETE);
+ if (SUCCEEDED(hr))
+ {
+ VBoxNetCfgWinFilterLimitWorkaround();
+ NonStandardLog("NetLwf will be installed ...\n");
+ hr = vboxNetCfgWinInstallInfAndComponent(pNc, VBOXNETCFGWIN_NETLWF_ID,
+ &GUID_DEVCLASS_NETSERVICE,
+ &pwszInfFullPath,
+ 1,
+ NULL);
+ }
+ return hr;
+}
+
+VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinGenHostonlyConnectionName(IN PCWSTR pwszDevName, OUT WCHAR *pwszBuf,
+ IN ULONG cwcBuf, OUT PULONG pcwcNeeded)
+{
+ /* Look for a suffix that we need to preserve. */
+ size_t const cwcDevName = RTUtf16Len(pwszDevName);
+ size_t offSuffix = cwcDevName;
+ while (offSuffix > 0 && pwszDevName[offSuffix - 1] != '#')
+ offSuffix--;
+ size_t const cwcSuffix = pwszDevName[offSuffix] != '#' ? 0 : cwcDevName - offSuffix;
+
+ /* Calculate required buffer size: */
+ size_t cwcNeeded = sizeof(VBOX_CONNECTION_NAME_WSZ) / sizeof(wchar_t) /* includes terminator */
+ + !!cwcSuffix /*space*/ + cwcSuffix;
+ if (pcwcNeeded)
+ *pcwcNeeded = (ULONG)cwcNeeded;
+
+ if (cwcNeeded <= cwcBuf)
+ {
+ memcpy(pwszBuf, VBOX_CONNECTION_NAME_WSZ, sizeof(VBOX_CONNECTION_NAME_WSZ));
+ if (cwcSuffix > 0)
+ {
+ size_t offDst = sizeof(VBOX_CONNECTION_NAME_WSZ) / sizeof(wchar_t) - 1;
+ pwszBuf[offDst++] = ' ';
+ memcpy(&pwszBuf[offDst], &pwszDevName[offSuffix], cwcSuffix * sizeof(wchar_t));
+ pwszBuf[offDst + cwcSuffix] = '\0';
+ }
+ return S_OK;
+ }
+ return E_FAIL;
+}
+
+static BOOL vboxNetCfgWinAdjustHostOnlyNetworkInterfacePriority(IN INetCfg *pNc, IN INetCfgComponent *pNcc, PVOID pContext)
+{
+ GUID * const pGuid = (GUID*)pContext;
+ RT_NOREF1(pNc);
+
+ /* Get component's binding. */
+ INetCfgComponentBindings *pNetCfgBindings = NULL;
+ HRESULT hr = pNcc->QueryInterface(IID_INetCfgComponentBindings, (PVOID *)&pNetCfgBindings);
+ if (SUCCEEDED(hr))
+ {
+ /* Get binding path enumerator reference. */
+ IEnumNetCfgBindingPath *pEnumNetCfgBindPath = NULL;
+ hr = pNetCfgBindings->EnumBindingPaths(EBP_BELOW, &pEnumNetCfgBindPath);
+ if (SUCCEEDED(hr))
+ {
+ bool fFoundIface = false;
+ hr = pEnumNetCfgBindPath->Reset();
+ do
+ {
+ INetCfgBindingPath *pNetCfgBindPath = NULL;
+ hr = pEnumNetCfgBindPath->Next(1, &pNetCfgBindPath, NULL);
+ if (hr == S_OK)
+ {
+ IEnumNetCfgBindingInterface *pEnumNetCfgBindIface;
+ hr = pNetCfgBindPath->EnumBindingInterfaces(&pEnumNetCfgBindIface);
+ if (hr == S_OK)
+ {
+ pEnumNetCfgBindIface->Reset();
+ do
+ {
+ INetCfgBindingInterface *pNetCfgBindIfce;
+ hr = pEnumNetCfgBindIface->Next(1, &pNetCfgBindIfce, NULL);
+ if (hr == S_OK)
+ {
+ INetCfgComponent *pNetCfgCompo;
+ hr = pNetCfgBindIfce->GetLowerComponent(&pNetCfgCompo);
+ if (hr == S_OK)
+ {
+ ULONG uComponentStatus;
+ hr = pNetCfgCompo->GetDeviceStatus(&uComponentStatus);
+ if (hr == S_OK)
+ {
+ GUID guid;
+ hr = pNetCfgCompo->GetInstanceGuid(&guid);
+ if ( hr == S_OK
+ && guid == *pGuid)
+ {
+ hr = pNetCfgBindings->MoveAfter(pNetCfgBindPath, NULL);
+ if (FAILED(hr))
+ NonStandardLogFlow(("Unable to move interface: %Rhrc\n", hr));
+ fFoundIface = true;
+
+ /*
+ * Enable binding paths for host-only adapters bound to bridged filter
+ * (see @bugref{8140}).
+ */
+ HRESULT hr2;
+ LPWSTR pwszHwId = NULL;
+ if ((hr2 = pNcc->GetId(&pwszHwId)) != S_OK)
+ NonStandardLogFlow(("Failed to get HW ID: %Rhrc\n", hr2));
+ else
+ {
+ /** @todo r=bird: Original was:
+ * _wcsnicmp(pwszHwId, VBOXNETCFGWIN_NETLWF_ID, sizeof(VBOXNETCFGWIN_NETLWF_ID)/2)
+ * which is the same as _wcsicmp. Not sure if this was accidental, but it's not the
+ * only one in this code area (VBoxNetFltNobj.cpp had some too IIRC). */
+ if (RTUtf16ICmp(pwszHwId, VBOXNETCFGWIN_NETLWF_ID) != 0)
+ NonStandardLogFlow(("Ignoring component %ls\n", pwszHwId));
+ else if ((hr2 = pNetCfgBindPath->IsEnabled()) != S_FALSE)
+ NonStandardLogFlow(("Already enabled binding path: %Rhrc\n", hr2));
+ else if ((hr2 = pNetCfgBindPath->Enable(TRUE)) != S_OK)
+ NonStandardLogFlow(("Failed to enable binding path: %Rhrc\n", hr2));
+ else
+ NonStandardLogFlow(("Enabled binding path\n"));
+ CoTaskMemFree(pwszHwId);
+ }
+ }
+ }
+ pNetCfgCompo->Release();
+ }
+ else
+ NonStandardLogFlow(("GetLowerComponent failed: %Rhrc\n", hr));
+ pNetCfgBindIfce->Release();
+ }
+ else
+ {
+ if (hr == S_FALSE) /* No more binding interfaces? */
+ hr = S_OK;
+ else
+ NonStandardLogFlow(("Next binding interface failed: %Rhrc\n", hr));
+ break;
+ }
+ } while (!fFoundIface);
+ pEnumNetCfgBindIface->Release();
+ }
+ else
+ NonStandardLogFlow(("EnumBindingInterfaces failed: %Rhrc\n", hr));
+ pNetCfgBindPath->Release();
+ }
+ else
+ {
+ if (hr == S_FALSE) /* No more binding paths? */
+ hr = S_OK;
+ else
+ NonStandardLogFlow(("Next bind path failed: %Rhrc\n", hr));
+ break;
+ }
+ } while (!fFoundIface);
+ pEnumNetCfgBindPath->Release();
+ }
+ else
+ NonStandardLogFlow(("EnumBindingPaths failed: %Rhrc\n", hr));
+ pNetCfgBindings->Release();
+ }
+ else
+ NonStandardLogFlow(("QueryInterface for IID_INetCfgComponentBindings failed: %Rhrc\n", hr));
+ return TRUE;
+}
+
+/** Callback for SetupDiSetDeviceInstallParams used by
+ * vboxNetCfgWinCreateHostOnlyNetworkInterface */
+static UINT WINAPI vboxNetCfgWinPspFileCallback(PVOID Context, UINT Notification, UINT_PTR Param1, UINT_PTR Param2)
+{
+ switch (Notification)
+ {
+ case SPFILENOTIFY_TARGETNEWER:
+ case SPFILENOTIFY_TARGETEXISTS:
+ return TRUE;
+ }
+ return SetupDefaultQueueCallbackW(Context, Notification, Param1, Param2);
+}
+
+
+
+
+/* The original source of the VBoxNetAdp adapter creation/destruction code has the following copyright: */
+/*
+ Copyright 2004 by the Massachusetts Institute of Technology
+
+ All rights reserved.
+
+ Permission to use, copy, modify, and distribute this software and its
+ documentation for any purpose and without fee is hereby granted,
+ provided that the above copyright notice appear in all copies and that
+ both that copyright notice and this permission notice appear in
+ supporting documentation, and that the name of the Massachusetts
+ Institute of Technology (M.I.T.) not be used in advertising or publicity
+ pertaining to distribution of the software without specific, written
+ prior permission.
+
+ M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+ M.I.T. BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ SOFTWARE.
+*/
+
+
+/**
+ * Use the IShellFolder API to rename the connection.
+ */
+static HRESULT rename_shellfolder(PCWSTR pwszGuid, PCWSTR pwszNewName)
+{
+ /* Build the display name in the form "::{GUID}". Do this first in case it overflows. */
+ WCHAR wszAdapterGuid[MAX_PATH + 2] = {0};
+ ssize_t cwc = RTUtf16Printf(wszAdapterGuid, RT_ELEMENTS(wszAdapterGuid), "::%ls", pwszGuid);
+ if (cwc < 0)
+ return E_INVALIDARG;
+
+ /* This is the GUID for the network connections folder. It is constant.
+ * {7007ACC7-3202-11D1-AAD2-00805FC1270E} */
+ const GUID MY_CLSID_NetworkConnections = {
+ 0x7007ACC7, 0x3202, 0x11D1, {
+ 0xAA, 0xD2, 0x00, 0x80, 0x5F, 0xC1, 0x27, 0x0E
+ }
+ };
+
+ /* Create an instance of the network connections folder. */
+ IShellFolder *pShellFolder = NULL;
+ HRESULT hr = CoCreateInstance(MY_CLSID_NetworkConnections, NULL, CLSCTX_INPROC_SERVER, IID_IShellFolder,
+ reinterpret_cast<LPVOID *>(&pShellFolder));
+ if (SUCCEEDED(hr))
+ {
+ /* Parse the display name. */
+ LPITEMIDLIST pidl = NULL;
+ hr = pShellFolder->ParseDisplayName(NULL, NULL, wszAdapterGuid, NULL, &pidl, NULL);
+ if (SUCCEEDED(hr))
+ hr = pShellFolder->SetNameOf(NULL, pidl, pwszNewName, SHGDN_NORMAL, &pidl);
+ CoTaskMemFree(pidl);
+ pShellFolder->Release();
+ }
+ return hr;
+}
+
+/**
+ * Loads a system DLL.
+ *
+ * @returns Module handle or NULL
+ * @param pwszName The DLL name.
+ */
+static HMODULE loadSystemDll(const wchar_t *pwszName)
+{
+ WCHAR wszPath[MAX_PATH];
+ UINT cwcPath = GetSystemDirectoryW(wszPath, RT_ELEMENTS(wszPath));
+ size_t cwcName = RTUtf16Len(pwszName) + 1;
+ if (cwcPath + 1 + cwcName > RT_ELEMENTS(wszPath))
+ return NULL;
+
+ wszPath[cwcPath++] = '\\';
+ memcpy(&wszPath[cwcPath], pwszName, cwcName * sizeof(wszPath[0]));
+ return LoadLibraryW(wszPath);
+}
+
+static bool vboxNetCfgWinDetectStaleConnection(PCWSTR pwszName)
+{
+ HKEY hKeyAdapters = NULL;
+ LSTATUS lrc = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+ L"SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}",
+ 0 /*ulOptions*/, KEY_ALL_ACCESS, &hKeyAdapters);
+ if (lrc != ERROR_SUCCESS)
+ return false;
+
+ bool fFailureImminent = false;
+ for (DWORD i = 0; !fFailureImminent; ++i)
+ {
+ WCHAR wszAdapterSubKeyName[MAX_PATH];
+ DWORD cwcAdapterSubKeyName = MAX_PATH;
+ lrc = RegEnumKeyEx(hKeyAdapters, i, wszAdapterSubKeyName, &cwcAdapterSubKeyName, NULL, NULL, NULL, NULL);
+ if (lrc != ERROR_SUCCESS)
+ break;
+
+ HKEY hKeyAdapter = NULL;
+ lrc = RegOpenKeyEx(hKeyAdapters, wszAdapterSubKeyName, 0, KEY_ALL_ACCESS, &hKeyAdapter);
+ if (lrc == ERROR_SUCCESS)
+ {
+ HKEY hKeyConnection = NULL;
+ lrc = RegOpenKeyEx(hKeyAdapter, L"Connection", 0, KEY_ALL_ACCESS, &hKeyConnection);
+ if (lrc == ERROR_SUCCESS)
+ {
+ WCHAR wszCurName[MAX_PATH + 1];
+ DWORD cbCurName = sizeof(wszCurName) - sizeof(WCHAR);
+ DWORD dwType = REG_SZ;
+ lrc = RegQueryValueEx(hKeyConnection, L"Name", NULL, NULL, (LPBYTE)wszCurName, &cbCurName);
+ if ( lrc == ERROR_SUCCESS
+ /** @todo r=bird: The original code didn't do any value type checks, thus allowing all SZ types. */
+ && (dwType == REG_SZ || dwType == REG_EXPAND_SZ || dwType == REG_MULTI_SZ))
+ {
+ wszCurName[MAX_PATH] = '\0'; /* returned values doesn't necessarily need to be terminated */
+
+ if (RTUtf16ICmp(pwszName, pwszName) == 0)
+ fFailureImminent = true;
+ }
+ RegCloseKey(hKeyConnection);
+ }
+ RegCloseKey(hKeyAdapter);
+ }
+ }
+ RegCloseKey(hKeyAdapters);
+
+ return fFailureImminent;
+}
+
+VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinRenameConnection(LPWSTR pwszGuid, PCWSTR NewName)
+{
+ /*
+ * Before attempting to rename the connection, check if there is a stale
+ * connection with the same name. We must return ok, so the rest of
+ * configuration process proceeds normally.
+ */
+ if (vboxNetCfgWinDetectStaleConnection(NewName))
+ return S_OK;
+
+ /* First try the IShellFolder interface, which was unimplemented
+ * for the network connections folder before XP. */
+ HRESULT hrc = rename_shellfolder(pwszGuid, NewName);
+ if (hrc == E_NOTIMPL)
+ {
+/** @todo that code doesn't seem to work! */
+ /* The IShellFolder interface is not implemented on this platform.
+ * Try the (undocumented) HrRenameConnection API in the netshell
+ * library. */
+ CLSID clsid;
+ hrc = CLSIDFromString((LPOLESTR)pwszGuid, &clsid);
+ if (FAILED(hrc))
+ return E_FAIL;
+
+ HINSTANCE hNetShell = loadSystemDll(L"netshell.dll");
+ if (hNetShell == NULL)
+ return E_FAIL;
+
+ typedef HRESULT (WINAPI *PFNHRRENAMECONNECTION)(const GUID *, PCWSTR);
+ PFNHRRENAMECONNECTION pfnRenameConnection = (PFNHRRENAMECONNECTION)GetProcAddress(hNetShell, "HrRenameConnection");
+ if (pfnRenameConnection != NULL)
+ hrc = pfnRenameConnection(&clsid, NewName);
+ else
+ hrc = E_FAIL;
+
+ FreeLibrary(hNetShell);
+ }
+ if (FAILED(hrc))
+ return hrc;
+ return S_OK;
+}
+
+
+VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinRemoveHostOnlyNetworkInterface(IN const GUID *pGUID, OUT BSTR *pBstrErrMsg)
+{
+ HRESULT hrc = S_OK;
+ com::Bstr bstrError;
+
+ do /* break non-loop */
+ {
+ WCHAR wszPnPInstanceId[512] = {0};
+
+ /*
+ * We have to find the device instance ID through a registry search
+ */
+ HKEY hkeyNetwork = NULL;
+ HKEY hkeyConnection = NULL;
+ do /* another non-loop for breaking out of */
+ {
+ WCHAR wszGuid[50];
+ int cwcGuid = StringFromGUID2(*pGUID, wszGuid, RT_ELEMENTS(wszGuid));
+ if (!cwcGuid)
+ SetErrBreak(("Failed to create a Guid string"));
+
+ WCHAR wszRegLocation[128 + RT_ELEMENTS(wszGuid)];
+ RTUtf16Printf(wszRegLocation, RT_ELEMENTS(wszRegLocation),
+ "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}\\%ls", wszGuid);
+
+ LSTATUS lrc = RegOpenKeyExW(HKEY_LOCAL_MACHINE, wszRegLocation, 0, KEY_READ, &hkeyNetwork);
+ if (lrc != ERROR_SUCCESS || !hkeyNetwork)
+ SetErrBreak(("Host interface network is not found in registry (%S): lrc=%u [1]", wszRegLocation, lrc));
+
+ lrc = RegOpenKeyExW(hkeyNetwork, L"Connection", 0, KEY_READ, &hkeyConnection);
+ if (lrc != ERROR_SUCCESS || !hkeyConnection)
+ SetErrBreak(("Host interface network is not found in registry (%S): lrc=%u [2]", wszRegLocation, lrc));
+
+ DWORD cbValue = sizeof(wszPnPInstanceId) - sizeof(WCHAR);
+ DWORD dwType = ~0U;
+ lrc = RegQueryValueExW(hkeyConnection, L"PnPInstanceID", NULL, &dwType, (LPBYTE)wszPnPInstanceId, &cbValue);
+ if (lrc != ERROR_SUCCESS || dwType != REG_SZ)
+ SetErrBreak(("Host interface network is not found in registry (%S): lrc=%u, dwType=%u [3]",
+ wszRegLocation, lrc, dwType));
+ } while (0);
+
+ if (hkeyConnection)
+ RegCloseKey(hkeyConnection);
+ if (hkeyNetwork)
+ RegCloseKey(hkeyNetwork);
+ if (FAILED(hrc))
+ break;
+
+ /*
+ * Now we are going to enumerate all network devices and
+ * wait until we encounter the right device instance ID
+ */
+ HDEVINFO hDeviceInfo = INVALID_HANDLE_VALUE;
+ do /* break-only, not-a-loop */
+ {
+ BOOL ok;
+
+ /* initialize the structure size */
+ SP_DEVINFO_DATA DeviceInfoData = { sizeof(DeviceInfoData) };
+
+ /* copy the net class GUID */
+ GUID netGuid;
+ memcpy(&netGuid, &GUID_DEVCLASS_NET, sizeof(GUID_DEVCLASS_NET));
+
+ /* return a device info set contains all installed devices of the Net class */
+ hDeviceInfo = SetupDiGetClassDevs(&netGuid, NULL, NULL, DIGCF_PRESENT);
+ if (hDeviceInfo == INVALID_HANDLE_VALUE)
+ SetErrBreak(("SetupDiGetClassDevs failed (0x%08X)", GetLastError()));
+
+ /* Enumerate the driver info list. */
+ bool fFound = false;
+ for (DWORD index = 0; !fFound; index++)
+ {
+ if (!SetupDiEnumDeviceInfo(hDeviceInfo, index, &DeviceInfoData))
+ {
+ if (GetLastError() == ERROR_NO_MORE_ITEMS)
+ break;
+ continue;
+ }
+
+ /* try to get the hardware ID registry property */
+ DWORD cbValue = 0;
+ if (SetupDiGetDeviceRegistryPropertyW(hDeviceInfo,
+ &DeviceInfoData,
+ SPDRP_HARDWAREID,
+ NULL,
+ NULL,
+ 0,
+ &cbValue))
+ continue; /* Something is wrong. This shouldn't have worked with a NULL buffer! */
+ if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
+ continue;
+
+ WCHAR *pwszzDeviceHwId = (WCHAR *)RTMemAllocZ(cbValue + sizeof(WCHAR) * 2);
+ if (!pwszzDeviceHwId)
+ break;
+ if (SetupDiGetDeviceRegistryPropertyW(hDeviceInfo,
+ &DeviceInfoData,
+ SPDRP_HARDWAREID,
+ NULL,
+ (PBYTE)pwszzDeviceHwId,
+ cbValue,
+ &cbValue))
+ {
+ /* search the string list. */
+ for (WCHAR *pwszCurHwId = pwszzDeviceHwId;
+ (uintptr_t)pwszCurHwId - (uintptr_t)pwszzDeviceHwId < cbValue && *pwszCurHwId != L'\0';
+ pwszCurHwId += RTUtf16Len(pwszCurHwId) + 1)
+ if (RTUtf16ICmp(DRIVERHWID, pwszCurHwId) == 0)
+ {
+ /* get the device instance ID */
+ WCHAR wszDevId[MAX_DEVICE_ID_LEN];
+ if (CM_Get_Device_IDW(DeviceInfoData.DevInst, wszDevId, MAX_DEVICE_ID_LEN, 0) == CR_SUCCESS)
+ {
+ /* compare to what we determined before */
+ if (RTUtf16Cmp(wszDevId, wszPnPInstanceId) == 0)
+ {
+ fFound = true;
+ break;
+ }
+ }
+ }
+ }
+ RTMemFree(pwszzDeviceHwId);
+ }
+
+ if (!fFound)
+ SetErrBreak(("Host Interface Network driver not found (0x%08X)", GetLastError()));
+
+ ok = SetupDiSetSelectedDevice(hDeviceInfo, &DeviceInfoData);
+ if (!ok)
+ SetErrBreak(("SetupDiSetSelectedDevice failed (0x%08X)", GetLastError()));
+
+ ok = SetupDiCallClassInstaller(DIF_REMOVE, hDeviceInfo, &DeviceInfoData);
+ if (!ok)
+ SetErrBreak(("SetupDiCallClassInstaller (DIF_REMOVE) failed (0x%08X)", GetLastError()));
+ } while (0);
+
+ /* clean up the device info set */
+ if (hDeviceInfo != INVALID_HANDLE_VALUE)
+ SetupDiDestroyDeviceInfoList (hDeviceInfo);
+ } while (0);
+
+ if (pBstrErrMsg)
+ {
+ *pBstrErrMsg = NULL;
+ if (bstrError.isNotEmpty())
+ bstrError.detachToEx(pBstrErrMsg);
+ }
+ return hrc;
+}
+
+VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinUpdateHostOnlyNetworkInterface(LPCWSTR pcsxwInf, BOOL *pfRebootRequired, LPCWSTR pcsxwId)
+{
+ return VBoxDrvCfgDrvUpdate(pcsxwId, pcsxwInf, pfRebootRequired);
+}
+
+static const char *vboxNetCfgWinGetStateText(DWORD dwState)
+{
+ switch (dwState)
+ {
+ case SERVICE_STOPPED: return "is not running";
+ case SERVICE_STOP_PENDING: return "is stopping";
+ case SERVICE_CONTINUE_PENDING: return "continue is pending";
+ case SERVICE_PAUSE_PENDING: return "pause is pending";
+ case SERVICE_PAUSED: return "is paused";
+ case SERVICE_RUNNING: return "is running";
+ case SERVICE_START_PENDING: return "is starting";
+ }
+ return "state is invalid";
+}
+
+static DWORD vboxNetCfgWinGetNetSetupState(SC_HANDLE hService)
+{
+ SERVICE_STATUS status;
+ status.dwCurrentState = SERVICE_RUNNING;
+ if (hService) {
+ if (QueryServiceStatus(hService, &status))
+ NonStandardLogFlow(("NetSetupSvc %s\n", vboxNetCfgWinGetStateText(status.dwCurrentState)));
+ else
+ NonStandardLogFlow(("QueryServiceStatus failed (0x%x)\n", GetLastError()));
+ }
+ return status.dwCurrentState;
+}
+
+DECLINLINE(bool) vboxNetCfgWinIsNetSetupRunning(SC_HANDLE hService)
+{
+ return vboxNetCfgWinGetNetSetupState(hService) == SERVICE_RUNNING;
+}
+
+DECLINLINE(bool) vboxNetCfgWinIsNetSetupStopped(SC_HANDLE hService)
+{
+ return vboxNetCfgWinGetNetSetupState(hService) == SERVICE_STOPPED;
+}
+
+typedef struct
+{
+ BSTR bstrName;
+ GUID *pGuid;
+ HRESULT hr;
+} RENAMING_CONTEXT;
+
+static BOOL vboxNetCfgWinRenameHostOnlyNetworkInterface(IN INetCfg *pNc, IN INetCfgComponent *pNcc, PVOID pContext)
+{
+ RT_NOREF1(pNc);
+ RENAMING_CONTEXT *pParams = (RENAMING_CONTEXT *)pContext;
+
+ GUID guid;
+ pParams->hr = pNcc->GetInstanceGuid(&guid);
+ if ( pParams->hr == S_OK && guid == *pParams->pGuid)
+ {
+ /* Located our component, rename it */
+ pParams->hr = pNcc->SetDisplayName(pParams->bstrName);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/**
+ * Enumerate all host-only adapters collecting their names into a set, then
+ * come up with the next available name by taking the first unoccupied index.
+ */
+static HRESULT vboxNetCfgWinNextAvailableDevName(com::Bstr *pbstrName)
+{
+ SP_DEVINFO_DATA DeviceInfoData = { sizeof(SP_DEVINFO_DATA) };
+ HDEVINFO hDeviceInfoSet = SetupDiGetClassDevsW(&GUID_DEVCLASS_NET, NULL, NULL, DIGCF_PRESENT);
+ if (hDeviceInfoSet == INVALID_HANDLE_VALUE)
+ return HRESULT_FROM_WIN32(GetLastError());
+
+ typedef struct VBOXDEVNAMEENTRY
+ {
+ RTLISTNODE ListEntry;
+ WCHAR wszDevName[64];
+ WCHAR wcZeroParanoia;
+ } VBOXDEVNAMEENTRY;
+
+#if 0
+ /*
+ * Build a list of names starting with HOSTONLY_ADAPTER_NAME_WSZ belonging to our device.
+ */
+ RTLISTANCHOR Head; /* VBOXDEVNAMEENTRY */
+ RTListInit(&Head);
+ HRESULT hrc = S_OK;
+#else
+ /*
+ * Build a bitmap of in-use index values of devices starting with HOSTONLY_ADAPTER_NAME_WSZ.
+ * Reserving 0 for one w/o a suffix, and marking 1 as unusable.
+ */
+ uint64_t bmIndexes[_32K / 64]; /* 4KB - 32767 device should be sufficient. */
+ RT_ZERO(bmIndexes);
+ ASMBitSet(&bmIndexes, 1);
+#endif
+ for (DWORD i = 0; SetupDiEnumDeviceInfo(hDeviceInfoSet, i, &DeviceInfoData); ++i)
+ {
+ /* Should be more than enough for both our device id and our device name, we do not care about the rest */
+ VBOXDEVNAMEENTRY Entry = { { 0, 0 }, L"", 0 }; /* (initialize it to avoid the wrath of asan) */
+ if (!SetupDiGetDeviceRegistryPropertyW(hDeviceInfoSet, &DeviceInfoData, SPDRP_HARDWAREID,
+ NULL, (PBYTE)Entry.wszDevName, sizeof(Entry.wszDevName), NULL))
+ continue;
+
+ /* Ignore everything except our host-only adapters */
+ if (RTUtf16ICmp(Entry.wszDevName, DRIVERHWID) == 0)
+ {
+ if ( SetupDiGetDeviceRegistryPropertyW(hDeviceInfoSet, &DeviceInfoData, SPDRP_FRIENDLYNAME,
+ NULL, (PBYTE)Entry.wszDevName, sizeof(Entry.wszDevName), NULL)
+ || SetupDiGetDeviceRegistryPropertyW(hDeviceInfoSet, &DeviceInfoData, SPDRP_DEVICEDESC,
+ NULL, (PBYTE)Entry.wszDevName, sizeof(Entry.wszDevName), NULL))
+ {
+ /* We can ignore any host-only adapter with a non-standard name. */
+ if (RTUtf16NICmp(Entry.wszDevName, HOSTONLY_ADAPTER_NAME_WSZ,
+ RT_ELEMENTS(HOSTONLY_ADAPTER_NAME_WSZ) - 1) == 0)
+ {
+#if 0
+ VBOXDEVNAMEENTRY *pEntry = (VBOXDEVNAMEENTRY *)RTMemDup(&Entry, sizeof(Entry));
+ if (pEntry)
+ RTListAppend(&Head, &pEntry->ListEntry);
+ else
+ {
+ hrc = E_OUTOFMEMORY;
+ break;
+ }
+#else
+ WCHAR const *pwc = &Entry.wszDevName[RT_ELEMENTS(HOSTONLY_ADAPTER_NAME_WSZ) - 1];
+
+ /* skip leading space */
+ WCHAR wc = *pwc;
+ while (wc == L' ' || wc == L'\t' || wc == L'\n' || wc == L'\r')
+ wc = *++pwc;
+
+ /* If end of string, use index 0. */
+ if (wc == L'\0')
+ ASMBitSet(bmIndexes, 0);
+
+ /* Hash and digit? */
+ else if (wc == L'#')
+ {
+ wc = *++pwc;
+ while (wc == L' ' || wc == L'\t' || wc == L'\n' || wc == L'\r') /* just in case */
+ wc = *++pwc;
+ if (wc >= L'0' && wc <= L'9')
+ {
+ /* Convert what we can to a number and mark it as allocated in the bitmap. */
+ uint64_t uIndex = wc - L'0';
+ while ((wc = *++pwc) >= L'0' && wc <= L'9')
+ uIndex = uIndex * 10 + wc - L'0';
+ if (uIndex < sizeof(bmIndexes) * 8 && uIndex > 0)
+ ASMBitSet(bmIndexes, (int32_t)uIndex);
+ }
+ }
+#endif
+ }
+ }
+ }
+ }
+#if 0
+ if (SUCCEEDED(hrc))
+ {
+ /*
+ * First try a name w/o an index, then try index #2 and up.
+ *
+ * Note! We have to use ASCII/UTF-8 strings here as Bstr will confuse WCHAR
+ * with BSTR/OLECHAR strings and use SysAllocString to duplicate it .
+ */
+ char szName[sizeof(HOSTONLY_ADAPTER_NAME_SZ " #4294967296") + 32] = HOSTONLY_ADAPTER_NAME_SZ;
+ size_t const cchBase = sizeof(HOSTONLY_ADAPTER_NAME_SZ) - 1;
+ for (DWORD idx = 2;; idx++)
+ {
+ bool fFound = false;
+ VBOXDEVNAMEENTRY *pCur;
+ RTListForEach(&Head, pCur, VBOXDEVNAMEENTRY, ListEntry)
+ {
+ fFound = RTUtf16ICmpAscii(pCur->wszDevName, szName) == 0;
+ if (fFound)
+ {
+ hrc = pbstrName->assignEx(szName);
+ break;
+ }
+ }
+ if (fFound)
+ break;
+ RTStrPrintf(&szName[cchBase], sizeof(szName) - cchBase, " #%u", idx);
+ }
+ }
+
+ VBOXDEVNAMEENTRY *pFirst;
+ while ((pFirst = RTListRemoveFirst(&Head, VBOXDEVNAMEENTRY, ListEntry)) != NULL)
+ RTMemFree(pFirst);
+
+#else
+ /*
+ * Find an unused index value and format the corresponding name.
+ */
+ HRESULT hrc;
+ int32_t iBit = ASMBitFirstClear(bmIndexes, sizeof(bmIndexes) * 8);
+ if (iBit >= 0)
+ {
+ if (iBit == 0)
+ hrc = pbstrName->assignEx(HOSTONLY_ADAPTER_NAME_SZ); /* Not _WSZ! */
+ else
+ hrc = pbstrName->printfNoThrow(HOSTONLY_ADAPTER_NAME_SZ " #%u", iBit);
+ }
+ else
+ {
+ NonStandardLogFlow(("vboxNetCfgWinNextAvailableDevName: no unused index in the first 32K!\n"));
+ hrc = E_FAIL;
+ }
+#endif
+
+ if (hDeviceInfoSet)
+ SetupDiDestroyDeviceInfoList(hDeviceInfoSet);
+ return hrc;
+}
+
+static HRESULT vboxNetCfgWinCreateHostOnlyNetworkInterface(IN LPCWSTR pwszInfPath, IN bool fIsInfPathFile,
+ IN BSTR pBstrDesiredName,
+ OUT GUID *pGuid, OUT BSTR *pBstrName, OUT BSTR *pBstrErrMsg)
+{
+ com::Bstr bstrError;
+
+ /* Determine the interface name. We make a copy of the input here for
+ renaming reasons, see futher down. */
+ com::Bstr bstrNewInterfaceName;
+ HRESULT hrc;
+ if (SysStringLen(pBstrDesiredName) != 0)
+ hrc = bstrNewInterfaceName.assignEx(pBstrDesiredName);
+ else
+ {
+ hrc = vboxNetCfgWinNextAvailableDevName(&bstrNewInterfaceName);
+ if (FAILED(hrc))
+ NonStandardLogFlow(("vboxNetCfgWinNextAvailableDevName failed with 0x%x\n", hrc));
+ }
+ if (FAILED(hrc))
+ return hrc;
+
+ WCHAR wszCfgGuidString[50] = {0};
+ WCHAR wszDevName[256 + 1] = {0};
+ SP_DEVINFO_DATA DeviceInfoData = { sizeof(DeviceInfoData) };
+ HDEVINFO hDeviceInfo = INVALID_HANDLE_VALUE;
+ PVOID pQueueCallbackContext = NULL;
+ BOOL fRegistered = FALSE;
+ BOOL destroyList = FALSE;
+ HKEY hkey = (HKEY)INVALID_HANDLE_VALUE;
+ LSTATUS lrcRet = ERROR_SUCCESS; /* the */
+
+ do /* non-loop, for breaking. */
+ {
+ /* copy the net class GUID */
+ GUID netGuid;
+ memcpy(&netGuid, &GUID_DEVCLASS_NET, sizeof(GUID_DEVCLASS_NET));
+
+ /* Create an empty device info set associated with the net class GUID: */
+ hDeviceInfo = SetupDiCreateDeviceInfoList(&netGuid, NULL);
+ if (hDeviceInfo == INVALID_HANDLE_VALUE)
+ SetErrBreak(("SetupDiCreateDeviceInfoList failed (%Rwc)", GetLastError()));
+
+ /* Translate the GUID to a class name: */
+ WCHAR wszClassName[MAX_PATH];
+ if (!SetupDiClassNameFromGuid(&netGuid, wszClassName, MAX_PATH, NULL))
+ SetErrBreak(("SetupDiClassNameFromGuid failed (%Rwc)", GetLastError()));
+
+ /* Create a device info element and add the new device instance key to registry: */
+ if (!SetupDiCreateDeviceInfo(hDeviceInfo, wszClassName, &netGuid, NULL, NULL, DICD_GENERATE_ID, &DeviceInfoData))
+ SetErrBreak(("SetupDiCreateDeviceInfo failed (%Rwc)", GetLastError()));
+
+ /* Select the newly created device info to be the currently selected member: */
+ if (!SetupDiSetSelectedDevice(hDeviceInfo, &DeviceInfoData))
+ SetErrBreak(("SetupDiSetSelectedDevice failed (%Rwc)", GetLastError()));
+
+ SP_DEVINSTALL_PARAMS DeviceInstallParams;
+ if (pwszInfPath)
+ {
+ /* get the device install parameters and disable filecopy */
+ DeviceInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
+ if (SetupDiGetDeviceInstallParams(hDeviceInfo, &DeviceInfoData, &DeviceInstallParams))
+ {
+ memset(DeviceInstallParams.DriverPath, 0, sizeof(DeviceInstallParams.DriverPath));
+ size_t pathLenght = wcslen(pwszInfPath) + 1/* null terminator */;
+ if (pathLenght < sizeof(DeviceInstallParams.DriverPath)/sizeof(DeviceInstallParams.DriverPath[0]))
+ {
+ memcpy(DeviceInstallParams.DriverPath, pwszInfPath, pathLenght*sizeof(DeviceInstallParams.DriverPath[0]));
+
+ if (fIsInfPathFile)
+ DeviceInstallParams.Flags |= DI_ENUMSINGLEINF;
+
+ if (!SetupDiSetDeviceInstallParams(hDeviceInfo, &DeviceInfoData, &DeviceInstallParams))
+ {
+ NonStandardLogFlow(("SetupDiSetDeviceInstallParams failed (%Rwc)\n", GetLastError()));
+ break;
+ }
+ }
+ else
+ {
+ NonStandardLogFlow(("SetupDiSetDeviceInstallParams faileed: INF path is too long\n"));
+ break;
+ }
+ }
+ else
+ NonStandardLogFlow(("SetupDiGetDeviceInstallParams failed (%Rwc)\n", GetLastError()));
+ }
+
+ /* build a list of class drivers */
+ if (!SetupDiBuildDriverInfoList(hDeviceInfo, &DeviceInfoData, SPDIT_CLASSDRIVER))
+ SetErrBreak(("SetupDiBuildDriverInfoList failed (%Rwc)", GetLastError()));
+
+ destroyList = TRUE;
+
+ /*
+ * Enumerate the driver info list.
+ */
+ /* For our purposes, 2k buffer is more than enough to obtain the
+ hardware ID of the VBoxNetAdp driver. */ /** @todo r=bird: The buffer isn't 2KB, it's 8KB, but whatever. */
+ DWORD detailBuf[2048];
+ SP_DRVINFO_DATA DriverInfoData = { sizeof(DriverInfoData) };
+ bool fFound = false;
+ for (DWORD index = 0; !fFound; index++)
+ {
+ /* If the function fails with last error set to ERROR_NO_MORE_ITEMS,
+ then we have reached the end of the list. Otherwise there was
+ something wrong with this particular driver. */
+ if (!SetupDiEnumDriverInfo(hDeviceInfo, &DeviceInfoData, SPDIT_CLASSDRIVER, index, &DriverInfoData))
+ {
+ if (GetLastError() == ERROR_NO_MORE_ITEMS)
+ break;
+ continue;
+ }
+
+ /* if we successfully find the hardware ID and it turns out to
+ * be the one for the loopback driver, then we are done. */
+ PSP_DRVINFO_DETAIL_DATA_W pDriverInfoDetail = (PSP_DRVINFO_DETAIL_DATA_W)detailBuf;
+ pDriverInfoDetail->cbSize = sizeof(SP_DRVINFO_DETAIL_DATA);
+ DWORD cbValue = 0;
+ if (SetupDiGetDriverInfoDetailW(hDeviceInfo,
+ &DeviceInfoData,
+ &DriverInfoData,
+ pDriverInfoDetail,
+ sizeof(detailBuf) - sizeof(detailBuf[0]),
+ &cbValue))
+ {
+ /* Sure that the HardwareID string list is properly zero terminated (paranoia). */
+ detailBuf[RT_ELEMENTS(detailBuf) - 1] = 0; AssertCompile(sizeof(detailBuf[0]) == sizeof(WCHAR) * 2);
+
+ /* pDriverInfoDetail->HardwareID is a MULTISZ string. Go through the whole
+ list and see if there is a match somewhere: */
+ for (WCHAR *pwszCurHwId = pDriverInfoDetail->HardwareID;
+ (uintptr_t)pwszCurHwId - (uintptr_t)pDriverInfoDetail < cbValue && *pwszCurHwId != L'\0';
+ pwszCurHwId += RTUtf16Len(pwszCurHwId) + 1)
+ if (RTUtf16ICmp(DRIVERHWID, pwszCurHwId) == 0)
+ {
+ fFound = true;
+ break;
+ }
+ }
+ }
+
+ if (!fFound)
+ SetErrBreak(("Could not find Host Interface Networking driver! Please reinstall"));
+
+ /* set the loopback driver to be the currently selected */
+ if (!SetupDiSetSelectedDriver(hDeviceInfo, &DeviceInfoData, &DriverInfoData))
+ SetErrBreak(("SetupDiSetSelectedDriver failed (%u)", GetLastError()));
+
+ /* register the phantom device to prepare for install */
+ if (!SetupDiCallClassInstaller(DIF_REGISTERDEVICE, hDeviceInfo, &DeviceInfoData))
+ SetErrBreak(("SetupDiCallClassInstaller failed (%u)", GetLastError()));
+
+ /* registered, but remove if errors occur in the following code */
+ fRegistered = TRUE;
+
+ /* ask the installer if we can install the device */
+ if (!SetupDiCallClassInstaller(DIF_ALLOW_INSTALL, hDeviceInfo, &DeviceInfoData))
+ {
+ if (GetLastError() != ERROR_DI_DO_DEFAULT)
+ SetErrBreak(("SetupDiCallClassInstaller (DIF_ALLOW_INSTALL) failed (%u)", GetLastError()));
+ /* that's fine */
+ }
+
+ /* get the device install parameters and disable filecopy */
+ DeviceInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
+ if (SetupDiGetDeviceInstallParams(hDeviceInfo, &DeviceInfoData, &DeviceInstallParams))
+ {
+ pQueueCallbackContext = SetupInitDefaultQueueCallback(NULL);
+ if (pQueueCallbackContext)
+ {
+ DeviceInstallParams.InstallMsgHandlerContext = pQueueCallbackContext;
+ DeviceInstallParams.InstallMsgHandler = (PSP_FILE_CALLBACK)vboxNetCfgWinPspFileCallback;
+ if (!SetupDiSetDeviceInstallParamsW(hDeviceInfo, &DeviceInfoData, &DeviceInstallParams))
+ {
+ DWORD winEr = GetLastError();
+ NonStandardLogFlow(("SetupDiSetDeviceInstallParamsW failed, winEr (%d)\n", winEr));
+ Assert(0);
+ }
+ }
+ else
+ {
+ DWORD winEr = GetLastError();
+ NonStandardLogFlow(("SetupInitDefaultQueueCallback failed, winEr (%d)\n", winEr));
+ }
+ }
+ else
+ {
+ DWORD winEr = GetLastError();
+ NonStandardLogFlow(("SetupDiGetDeviceInstallParams failed, winEr (%d)\n", winEr));
+ }
+
+ /* install the files first */
+ if (!SetupDiCallClassInstaller(DIF_INSTALLDEVICEFILES, hDeviceInfo, &DeviceInfoData))
+ SetErrBreak(("SetupDiCallClassInstaller (DIF_INSTALLDEVICEFILES) failed (%Rwc)", GetLastError()));
+
+ /* get the device install parameters and disable filecopy */
+ DeviceInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
+ if (SetupDiGetDeviceInstallParams(hDeviceInfo, &DeviceInfoData, &DeviceInstallParams))
+ {
+ DeviceInstallParams.Flags |= DI_NOFILECOPY;
+ if (!SetupDiSetDeviceInstallParamsW(hDeviceInfo, &DeviceInfoData, &DeviceInstallParams))
+ SetErrBreak(("SetupDiSetDeviceInstallParamsW failed (%Rwc)", GetLastError()));
+ }
+ /** @todo r=bird: Why isn't SetupDiGetDeviceInstallParams failure fatal here? */
+
+ /*
+ * Register any device-specific co-installers for this device,
+ */
+ if (!SetupDiCallClassInstaller(DIF_REGISTER_COINSTALLERS, hDeviceInfo, &DeviceInfoData))
+ SetErrBreak(("SetupDiCallClassInstaller (DIF_REGISTER_COINSTALLERS) failed (%Rwc)", GetLastError()));
+
+ /*
+ * install any installer-specified interfaces.
+ * and then do the real install
+ */
+ if (!SetupDiCallClassInstaller(DIF_INSTALLINTERFACES, hDeviceInfo, &DeviceInfoData))
+ SetErrBreak(("SetupDiCallClassInstaller (DIF_INSTALLINTERFACES) failed (%Rwc)", GetLastError()));
+
+ if (!SetupDiCallClassInstaller(DIF_INSTALLDEVICE, hDeviceInfo, &DeviceInfoData))
+ SetErrBreak(("SetupDiCallClassInstaller (DIF_INSTALLDEVICE) failed (%Rwc)", GetLastError()));
+
+ /*
+ * Query the instance ID; on Windows 10, the registry key may take a short
+ * while to appear. Microsoft recommends waiting for up to 5 seconds, but
+ * we want to be on the safe side, so let's wait for 20 seconds. Waiting
+ * longer is harmful as network setup service will shut down after a period
+ * of inactivity.
+ */
+ for (int retries = 0; retries < 2 * 20; ++retries)
+ {
+ Sleep(500); /* half second */
+
+ /* Figure out NetCfgInstanceId */
+ hkey = SetupDiOpenDevRegKey(hDeviceInfo, &DeviceInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DRV, KEY_READ);
+ if (hkey == INVALID_HANDLE_VALUE)
+ break;
+
+ DWORD cbSize = sizeof(wszCfgGuidString);
+ DWORD dwValueType = 0;
+ lrcRet = RegQueryValueExW(hkey, L"NetCfgInstanceId", NULL, &dwValueType, (LPBYTE)wszCfgGuidString, &cbSize);
+ /* As long as the return code is FILE_NOT_FOUND, sleep and retry. */
+ if (lrcRet != ERROR_FILE_NOT_FOUND)
+ break;
+
+ RegCloseKey(hkey);
+ hkey = (HKEY)INVALID_HANDLE_VALUE;
+ }
+
+ if (lrcRet == ERROR_FILE_NOT_FOUND)
+ {
+ hrc = E_ABORT;
+ break;
+ }
+
+ /*
+ * We need to check 'hkey' after we check 'lrcRet' to distinguish the case
+ * of failed SetupDiOpenDevRegKey from the case when we timed out.
+ */
+ if (hkey == INVALID_HANDLE_VALUE)
+ SetErrBreak(("SetupDiOpenDevRegKey failed (%Rwc)", GetLastError()));
+
+ if (lrcRet != ERROR_SUCCESS)
+ SetErrBreak(("Querying NetCfgInstanceId failed (%Rwc)", lrcRet));
+
+ NET_LUID luid;
+ HRESULT hrcSMRes = vboxNetCfgWinGetInterfaceLUID(hkey, &luid);
+
+ /* Close the key as soon as possible. See @bugref{7973}. */
+ RegCloseKey(hkey);
+ hkey = (HKEY)INVALID_HANDLE_VALUE;
+
+ if (FAILED(hrcSMRes))
+ {
+ /*
+ * The setting of Metric is not very important functionality,
+ * So we will not break installation process due to this error.
+ */
+ NonStandardLogFlow(("vboxNetCfgWinCreateHostOnlyNetworkInterface: Warning! "
+ "vboxNetCfgWinGetInterfaceLUID failed, default metric for new interface will not be set: %Rhrc\n",
+ hrcSMRes));
+ }
+ else
+ {
+ /*
+ * Set default metric value of interface to fix multicast issue
+ * See @bugref{6379} for details.
+ */
+ hrcSMRes = vboxNetCfgWinSetupMetric(&luid);
+ if (FAILED(hrcSMRes))
+ {
+ /*
+ * The setting of Metric is not very important functionality,
+ * So we will not break installation process due to this error.
+ */
+ NonStandardLogFlow(("vboxNetCfgWinCreateHostOnlyNetworkInterface: Warning! "
+ "vboxNetCfgWinSetupMetric failed, default metric for new interface will not be set: %Rhrc\n",
+ hrcSMRes));
+ }
+ }
+
+ /*
+ * We need to query the device name after we have succeeded in querying its
+ * instance ID to avoid similar waiting-and-retrying loop (see @bugref{7973}).
+ */
+ if (!SetupDiGetDeviceRegistryPropertyW(hDeviceInfo, &DeviceInfoData,
+ SPDRP_FRIENDLYNAME , /* IN DWORD Property,*/
+ NULL, /*OUT PDWORD PropertyRegDataType, OPTIONAL*/
+ (PBYTE)wszDevName, /*OUT PBYTE PropertyBuffer,*/
+ sizeof(wszDevName) - sizeof(WCHAR), /* IN DWORD PropertyBufferSize,*/
+ NULL /*OUT PDWORD RequiredSize OPTIONAL*/ ))
+ {
+ DWORD dwErr = GetLastError();
+ if (dwErr != ERROR_INVALID_DATA)
+ SetErrBreak(("SetupDiGetDeviceRegistryProperty failed (%Rwc)", dwErr));
+
+ RT_ZERO(wszDevName);
+ if (!SetupDiGetDeviceRegistryPropertyW(hDeviceInfo, &DeviceInfoData,
+ SPDRP_DEVICEDESC, /* IN DWORD Property,*/
+ NULL, /*OUT PDWORD PropertyRegDataType, OPTIONAL*/
+ (PBYTE)wszDevName, /*OUT PBYTE PropertyBuffer,*/
+ sizeof(wszDevName) - sizeof(WCHAR), /* IN DWORD PropertyBufferSize,*/
+ NULL /*OUT PDWORD RequiredSize OPTIONAL*/ ))
+ SetErrBreak(("SetupDiGetDeviceRegistryProperty failed (%Rwc)", GetLastError()));
+ }
+
+ /* No need to rename the device if the names match. */
+ if (RTUtf16Cmp(bstrNewInterfaceName.raw(), wszDevName) == 0)
+ bstrNewInterfaceName.setNull();
+
+#ifdef VBOXNETCFG_DELAYEDRENAME
+ /* Re-use wszDevName for device instance id retrieval. */
+ DWORD cwcReturned = 0;
+ RT_ZERO(wszDevName);
+ if (!SetupDiGetDeviceInstanceIdW(hDeviceInfo, &DeviceInfoData, wszDevName, RT_ELEMENTS(wszDevName) - 1, &cwcReturned))
+ SetErrBreak(("SetupDiGetDeviceInstanceId failed (%Rwc)", GetLastError()));
+#endif /* VBOXNETCFG_DELAYEDRENAME */
+ } while (0);
+
+ /*
+ * Cleanup.
+ */
+ if (hkey != INVALID_HANDLE_VALUE)
+ RegCloseKey(hkey);
+
+ if (pQueueCallbackContext)
+ SetupTermDefaultQueueCallback(pQueueCallbackContext);
+
+ if (hDeviceInfo != INVALID_HANDLE_VALUE)
+ {
+ /* an error has occurred, but the device is registered, we must remove it */
+ if (lrcRet != ERROR_SUCCESS && fRegistered)
+ SetupDiCallClassInstaller(DIF_REMOVE, hDeviceInfo, &DeviceInfoData);
+
+ SetupDiDeleteDeviceInfo(hDeviceInfo, &DeviceInfoData);
+
+ /* destroy the driver info list */
+#if 0
+ /* I've remove this, as I was consistently getting crashes in
+ * SetupDiDestroyDeviceInfoList otherwise during MSI installation
+ * (W10 build 19044, VBox r153431 + nocrt changes).
+ *
+ * Some details from windbg:
+ *
+ * (175e8.1596c): Access violation - code c0000005 (first chance)
+ * First chance exceptions are reported before any exception handling.
+ * This exception may be expected and handled.
+ * SETUPAPI!DereferenceClassDriverList+0x4e:
+ * 00007ffa`83e2a42a 834008ff add dword ptr [rax+8],0FFFFFFFFh ds:00000000`00000008=????????
+ * 0:006> k
+ * # Child-SP RetAddr Call Site
+ * 00 0000007e`ccd7c070 00007ffa`83e2a287 SETUPAPI!DereferenceClassDriverList+0x4e
+ * 01 0000007e`ccd7c0a0 00007ffa`56b96bd3 SETUPAPI!SetupDiDestroyDriverInfoList+0x117
+ * 02 0000007e`ccd7c0f0 00007ffa`56b982a3 MSIF170!vboxNetCfgWinCreateHostOnlyNetworkInterface+0xb23 [E:\vbox\svn\trunk\src\VBox\HostDrivers\VBoxNetFlt\win\cfg\VBoxNetCfg.cpp @ 3378]
+ * 03 0000007e`ccd7ef10 00007ffa`56b92cb8 MSIF170!VBoxNetCfgWinCreateHostOnlyNetworkInterface+0x53 [E:\vbox\svn\trunk\src\VBox\HostDrivers\VBoxNetFlt\win\cfg\VBoxNetCfg.cpp @ 3479]
+ * 04 0000007e`ccd7efc0 00007ffa`610f59d3 MSIF170!_createHostOnlyInterface+0x218 [E:\vbox\svn\trunk\src\VBox\Installer\win\InstallHelper\VBoxInstallHelper.cpp @ 1453]
+ * 05 0000007e`ccd7f260 00007ffa`610d80ac msi!CallCustomDllEntrypoint+0x2b
+ * 06 0000007e`ccd7f2d0 00007ffa`84567034 msi!CMsiCustomAction::CustomActionThread+0x34c
+ * 07 0000007e`ccd7f8c0 00007ffa`849a2651 KERNEL32!BaseThreadInitThunk+0x14
+ * 08 0000007e`ccd7f8f0 00000000`00000000 ntdll!RtlUserThreadStart+0x21
+ * 0:006> r
+ * rax=0000000000000000 rbx=0000025f4f03af90 rcx=00007ffa83f23b40
+ * rdx=0000025f4f03af90 rsi=0000025f5108be50 rdi=0000025f4f066f10
+ * rip=00007ffa83e2a42a rsp=0000007eccd7c070 rbp=00007ffa83f23000
+ * r8=0000007eccd7c0a8 r9=0000007eccd7c1f0 r10=0000000000000000
+ * r11=0000007eccd7c020 r12=0000007eccd7c3d0 r13=00007ffa83f23000
+ * r14=0000000000000000 r15=0000025f4f03af90
+ * iopl=0 nv up ei pl nz na po nc
+ * cs=0033 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010206
+ * SETUPAPI!DereferenceClassDriverList+0x4e:
+ * 00007ffa`83e2a42a 834008ff add dword ptr [rax+8],0FFFFFFFFh ds:00000000`00000008=????????
+ *
+ */
+ if (destroyList)
+ SetupDiDestroyDriverInfoList(hDeviceInfo, &DeviceInfoData, SPDIT_CLASSDRIVER);
+#else
+ RT_NOREF(destroyList);
+#endif
+
+ /* clean up the device info set */
+ SetupDiDestroyDeviceInfoList(hDeviceInfo);
+ }
+
+ /*
+ * Return the network connection GUID on success.
+ */
+ if (SUCCEEDED(hrc))
+ {
+ /** @todo r=bird: Some please explain this mess. It's not just returning a
+ * GUID here, it's a bit more than that. */
+ RENAMING_CONTEXT context;
+ context.hr = E_FAIL;
+
+ if (pGuid)
+ {
+ hrc = CLSIDFromString(wszCfgGuidString, (LPCLSID)pGuid);
+ if (FAILED(hrc))
+ NonStandardLogFlow(("CLSIDFromString failed, hrc (0x%x)\n", hrc));
+ }
+
+ INetCfg *pNetCfg = NULL;
+ LPWSTR pwszApp = NULL;
+ HRESULT hrc2 = VBoxNetCfgWinQueryINetCfg(&pNetCfg, TRUE, L"VirtualBox Host-Only Creation",
+ 30 * 1000, /* on Vista we often get 6to4svc.dll holding the lock, wait for 30 sec. */
+ /** @todo special handling for 6to4svc.dll ???, i.e. several retrieves */
+ &pwszApp);
+ if (hrc2 == S_OK)
+ {
+ if (bstrNewInterfaceName.isNotEmpty())
+ {
+ /* The assigned name does not match the desired one, rename the device */
+ context.bstrName = bstrNewInterfaceName.raw();
+ context.pGuid = pGuid;
+ hrc2 = vboxNetCfgWinEnumNetCfgComponents(pNetCfg, &GUID_DEVCLASS_NET,
+ vboxNetCfgWinRenameHostOnlyNetworkInterface, &context);
+ }
+ if (SUCCEEDED(hrc2))
+ hrc2 = vboxNetCfgWinEnumNetCfgComponents(pNetCfg, &GUID_DEVCLASS_NETSERVICE,
+ vboxNetCfgWinAdjustHostOnlyNetworkInterfacePriority, pGuid);
+ if (SUCCEEDED(hrc2))
+ hrc2 = vboxNetCfgWinEnumNetCfgComponents(pNetCfg, &GUID_DEVCLASS_NETTRANS,
+ vboxNetCfgWinAdjustHostOnlyNetworkInterfacePriority, pGuid);
+ if (SUCCEEDED(hrc2))
+ hrc2 = vboxNetCfgWinEnumNetCfgComponents(pNetCfg, &GUID_DEVCLASS_NETCLIENT,
+ vboxNetCfgWinAdjustHostOnlyNetworkInterfacePriority, pGuid);
+ if (SUCCEEDED(hrc2))
+ hrc2 = pNetCfg->Apply();
+ else
+ NonStandardLogFlow(("Enumeration failed, hrc2=%Rhrc\n", hrc2));
+
+ VBoxNetCfgWinReleaseINetCfg(pNetCfg, TRUE);
+ }
+ else if (hrc2 == NETCFG_E_NO_WRITE_LOCK && pwszApp)
+ {
+ NonStandardLogFlow(("Application '%ls' is holding the lock, failed\n", pwszApp));
+ CoTaskMemFree(pwszApp);
+ pwszApp = NULL;
+ }
+ else
+ NonStandardLogFlow(("VBoxNetCfgWinQueryINetCfg failed, hrc2=%Rhrc\n", hrc2));
+
+#ifndef VBOXNETCFG_DELAYEDRENAME
+ /* If the device has been successfully renamed, replace the name now. */
+ if (SUCCEEDED(hrc2) && SUCCEEDED(context.hr))
+ RTUtf16Copy(wszDevName, RT_ELEMENTS(wszDevName), pBstrDesiredName);
+
+ WCHAR wszConnectionName[128];
+ hrc2 = VBoxNetCfgWinGenHostonlyConnectionName(wszDevName, wszConnectionName, RT_ELEMENTS(wszConnectionName), NULL);
+ if (SUCCEEDED(hrc2))
+ hrc2 = VBoxNetCfgWinRenameConnection(wszCfgGuidString, wszConnectionName);
+#endif
+
+ /*
+ * Now, return the network connection GUID/name.
+ */
+ if (pBstrName)
+ {
+ *pBstrName = SysAllocString((const OLECHAR *)wszDevName);
+ if (!*pBstrName)
+ {
+ NonStandardLogFlow(("SysAllocString failed\n"));
+ hrc = E_OUTOFMEMORY;
+ }
+ }
+ }
+
+ if (pBstrErrMsg)
+ {
+ *pBstrErrMsg = NULL;
+ if (bstrError.isNotEmpty())
+ bstrError.detachToEx(pBstrErrMsg);
+ }
+ return hrc;
+}
+
+VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinCreateHostOnlyNetworkInterface(IN LPCWSTR pwszInfPath, IN bool fIsInfPathFile,
+ IN BSTR pwszDesiredName, OUT GUID *pGuid,
+ OUT BSTR *pBstrName, OUT BSTR *pBstrErrMsg)
+{
+ HRESULT hrc = vboxNetCfgWinCreateHostOnlyNetworkInterface(pwszInfPath, fIsInfPathFile, pwszDesiredName,
+ pGuid, pBstrName, pBstrErrMsg);
+ if (hrc == E_ABORT)
+ {
+ NonStandardLogFlow(("Timed out while waiting for NetCfgInstanceId, try again immediately...\n"));
+
+ /*
+ * This is the first time we fail to obtain NetCfgInstanceId, let us
+ * retry it once. It is needed to handle the situation when network
+ * setup fails to recognize the arrival of our device node while it
+ * is busy removing another host-only interface, and it gets stuck
+ * with no matching network interface created for our device node.
+ * See @bugref{7973} for details.
+ */
+ hrc = vboxNetCfgWinCreateHostOnlyNetworkInterface(pwszInfPath, fIsInfPathFile, pwszDesiredName,
+ pGuid, pBstrName, pBstrErrMsg);
+ if (hrc == E_ABORT)
+ {
+ NonStandardLogFlow(("Timed out again while waiting for NetCfgInstanceId, try again after a while...\n"));
+
+ /*
+ * This is the second time we fail to obtain NetCfgInstanceId, let us
+ * retry it once more. This time we wait to network setup service
+ * to go down before retrying. Hopefully it will resolve all error
+ * conditions. See @bugref{7973} for details.
+ */
+ SC_HANDLE hSCM = OpenSCManagerW(NULL, NULL, GENERIC_READ);
+ if (hSCM)
+ {
+ SC_HANDLE hService = OpenServiceW(hSCM, L"NetSetupSvc", GENERIC_READ);
+ if (hService)
+ {
+ for (int retries = 0; retries < 60 && !vboxNetCfgWinIsNetSetupStopped(hService); ++retries)
+ Sleep(1000);
+ CloseServiceHandle(hService);
+ hrc = vboxNetCfgWinCreateHostOnlyNetworkInterface(pwszInfPath, fIsInfPathFile, pwszDesiredName,
+ pGuid, pBstrName, pBstrErrMsg);
+ }
+ else
+ NonStandardLogFlow(("OpenService failed (%Rwc)\n", GetLastError()));
+ CloseServiceHandle(hSCM);
+ }
+ else
+ NonStandardLogFlow(("OpenSCManager failed (%Rwc)", GetLastError()));
+
+ /* Give up and report the error. */
+ if (hrc == E_ABORT)
+ {
+ if (pBstrErrMsg)
+ {
+ com::Bstr bstrError;
+ bstrError.printfNoThrow("Querying NetCfgInstanceId failed (ERROR_FILE_NOT_FOUND)");
+ bstrError.detachToEx(pBstrErrMsg);
+ }
+ hrc = E_FAIL;
+ }
+ }
+ }
+ return hrc;
+}
+
+static HRESULT vboxNetCfgWinGetLoopbackMetric(OUT DWORD *Metric)
+{
+ Assert(g_pfnInitializeIpInterfaceEntry != NULL);
+ Assert(g_pfnGetIpInterfaceEntry != NULL);
+
+ MIB_IPINTERFACE_ROW row;
+ RT_ZERO(row); /* paranoia */
+ g_pfnInitializeIpInterfaceEntry(&row);
+
+ row.Family = AF_INET;
+ row.InterfaceLuid.Info.IfType = IF_TYPE_SOFTWARE_LOOPBACK;
+
+ NETIO_STATUS dwErr = g_pfnGetIpInterfaceEntry(&row);
+ if (dwErr == NO_ERROR)
+ {
+ *Metric = row.Metric;
+ return S_OK;
+ }
+ return HRESULT_FROM_WIN32(dwErr);
+}
+
+static HRESULT vboxNetCfgWinSetInterfaceMetric(IN NET_LUID *pInterfaceLuid, IN DWORD metric)
+{
+ Assert(g_pfnInitializeIpInterfaceEntry != NULL);
+ Assert(g_pfnSetIpInterfaceEntry != NULL);
+
+ MIB_IPINTERFACE_ROW newRow;
+ RT_ZERO(newRow); /* paranoia */
+ g_pfnInitializeIpInterfaceEntry(&newRow);
+
+ // identificate the interface to change
+ newRow.InterfaceLuid = *pInterfaceLuid;
+ newRow.Family = AF_INET;
+
+ // changed settings
+ newRow.UseAutomaticMetric = false;
+ newRow.Metric = metric;
+
+ // change settings
+ NETIO_STATUS dwErr = g_pfnSetIpInterfaceEntry(&newRow);
+ if (dwErr == NO_ERROR)
+ return S_OK;
+ return HRESULT_FROM_WIN32(dwErr);
+}
+
+static HRESULT vboxNetCfgWinSetupMetric(IN NET_LUID* pLuid)
+{
+ HRESULT hrc = E_FAIL;
+ HMODULE hmod = loadSystemDll(L"Iphlpapi.dll");
+ if (hmod)
+ {
+ g_pfnInitializeIpInterfaceEntry = (PFNINITIALIZEIPINTERFACEENTRY)GetProcAddress(hmod, "InitializeIpInterfaceEntry");
+ g_pfnGetIpInterfaceEntry = (PFNGETIPINTERFACEENTRY) GetProcAddress(hmod, "GetIpInterfaceEntry");
+ g_pfnSetIpInterfaceEntry = (PFNSETIPINTERFACEENTRY) GetProcAddress(hmod, "SetIpInterfaceEntry");
+ Assert(g_pfnInitializeIpInterfaceEntry);
+ Assert(g_pfnGetIpInterfaceEntry);
+ Assert(g_pfnSetIpInterfaceEntry);
+
+ if ( g_pfnInitializeIpInterfaceEntry
+ && g_pfnGetIpInterfaceEntry
+ && g_pfnSetIpInterfaceEntry)
+ {
+ DWORD loopbackMetric = 0;
+ hrc = vboxNetCfgWinGetLoopbackMetric(&loopbackMetric);
+ if (SUCCEEDED(hrc))
+ hrc = vboxNetCfgWinSetInterfaceMetric(pLuid, loopbackMetric - 1);
+ }
+
+ g_pfnInitializeIpInterfaceEntry = NULL;
+ g_pfnSetIpInterfaceEntry = NULL;
+ g_pfnGetIpInterfaceEntry = NULL;
+
+ FreeLibrary(hmod);
+ }
+ return hrc;
+}
+
+static HRESULT vboxNetCfgWinGetInterfaceLUID(IN HKEY hKey, OUT NET_LUID *pLUID)
+{
+ if (pLUID == NULL)
+ return E_INVALIDARG;
+
+ DWORD dwLuidIndex = 0;
+ DWORD cbSize = sizeof(dwLuidIndex);
+ DWORD dwValueType = REG_DWORD; /** @todo r=bird: This is output only. No checked after the call. So, only for debugging? */
+ LSTATUS lrc = RegQueryValueExW(hKey, L"NetLuidIndex", NULL, &dwValueType, (LPBYTE)&dwLuidIndex, &cbSize);
+ if (lrc == ERROR_SUCCESS)
+ {
+ DWORD dwIfType = 0;
+ cbSize = sizeof(dwIfType);
+ dwValueType = REG_DWORD; /** @todo r=bird: This is output only. No checked after the call. So, only for debugging? */
+ lrc = RegQueryValueExW(hKey, L"*IfType", NULL, &dwValueType, (LPBYTE)&dwIfType, &cbSize);
+ if (lrc == ERROR_SUCCESS)
+ {
+ RT_ZERO(*pLUID);
+ pLUID->Info.IfType = dwIfType;
+ pLUID->Info.NetLuidIndex = dwLuidIndex;
+ return S_OK;
+ }
+ }
+
+ RT_ZERO(*pLUID);
+ return HRESULT_FROM_WIN32(lrc);
+}
+
+
+#ifdef VBOXNETCFG_DELAYEDRENAME
+VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinRenameHostOnlyConnection(IN const GUID *pGuid, IN LPCWSTR pwszId, OUT BSTR *pDevName)
+{
+ if (pDevName)
+ *pDevName = NULL;
+
+ HRESULT hr = S_OK; /** @todo r=bird: ODD return status for SetupDiCreateDeviceInfoList failures! */
+ HDEVINFO hDevInfo = SetupDiCreateDeviceInfoList(&GUID_DEVCLASS_NET, NULL);
+ if (hDevInfo != INVALID_HANDLE_VALUE)
+ {
+ SP_DEVINFO_DATA DevInfoData = { sizeof(SP_DEVINFO_DATA) };
+ if (SetupDiOpenDeviceInfo(hDevInfo, pwszId, NULL, 0, &DevInfoData))
+ {
+ WCHAR wszDevName[256 + 1] = {0};
+ DWORD err = ERROR_SUCCESS;
+ if (!SetupDiGetDeviceRegistryPropertyW(hDevInfo, &DevInfoData, SPDRP_FRIENDLYNAME, NULL /*PropertyRegDataType*/,
+ (PBYTE)wszDevName, sizeof(wszDevName) - sizeof(WCHAR) /*yes, bytes*/,
+ NULL /*RequiredSize*/))
+ {
+ err = GetLastError();
+ if (err == ERROR_INVALID_DATA)
+ {
+ RT_ZERO(wszDevName);
+ if (SetupDiGetDeviceRegistryPropertyW(hDevInfo, &DevInfoData, SPDRP_DEVICEDESC, NULL /*PropertyRegDataType*/,
+ (PBYTE)wszDevName, sizeof(wszDevName) - sizeof(WCHAR) /*yes, bytes*/,
+ NULL /*RequiredSize*/))
+ err = ERROR_SUCCESS;
+ else
+ err = GetLastError();
+ }
+ }
+ if (err == ERROR_SUCCESS)
+ {
+ WCHAR wszConnectionNewName[128] = {0};
+ hr = VBoxNetCfgWinGenHostonlyConnectionName(wszDevName, wszConnectionNewName,
+ RT_ELEMENTS(wszConnectionNewName), NULL);
+ if (SUCCEEDED(hr))
+ {
+ WCHAR wszGuid[50];
+ int cbWGuid = StringFromGUID2(*pGuid, wszGuid, RT_ELEMENTS(wszGuid));
+ if (cbWGuid)
+ {
+ hr = VBoxNetCfgWinRenameConnection(wszGuid, wszConnectionNewName);
+ if (FAILED(hr))
+ NonStandardLogFlow(("VBoxNetCfgWinRenameHostOnlyConnection: VBoxNetCfgWinRenameConnection failed (0x%x)\n", hr));
+ }
+ else
+ {
+ err = GetLastError();
+ hr = HRESULT_FROM_WIN32(err);
+ if (SUCCEEDED(hr))
+ hr = E_FAIL;
+ NonStandardLogFlow(("StringFromGUID2 failed err=%u, hr=0x%x\n", err, hr));
+ }
+ }
+ else
+ NonStandardLogFlow(("VBoxNetCfgWinRenameHostOnlyConnection: VBoxNetCfgWinGenHostonlyConnectionName failed (0x%x)\n", hr));
+ if (SUCCEEDED(hr) && pDevName)
+ {
+ *pDevName = SysAllocString((const OLECHAR *)wszDevName);
+ if (!*pDevName)
+ {
+ NonStandardLogFlow(("SysAllocString failed\n"));
+ hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
+ }
+ }
+ }
+ else
+ {
+ hr = HRESULT_FROM_WIN32(err);
+ NonStandardLogFlow(("VBoxNetCfgWinRenameHostOnlyConnection: SetupDiGetDeviceRegistryPropertyW failed (0x%x)\n", err));
+ }
+ }
+ else
+ {
+ DWORD err = GetLastError();
+ hr = HRESULT_FROM_WIN32(err);
+ NonStandardLogFlow(("VBoxNetCfgWinRenameHostOnlyConnection: SetupDiOpenDeviceInfo failed (0x%x)\n", err));
+ }
+ SetupDiDestroyDeviceInfoList(hDevInfo);
+ }
+
+ return hr;
+}
+#endif /* VBOXNETCFG_DELAYEDRENAME */
+
+#undef SetErrBreak
+
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/drv/Makefile.kup b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/Makefile.kup
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetAdp.inf b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetAdp.inf
new file mode 100644
index 00000000..9e13a198
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetAdp.inf
@@ -0,0 +1,95 @@
+; $Id: VBoxNetAdp.inf $
+;; @file
+; VBoxNetAdp.inf - VirtualBox Host-Only Driver inf file
+;
+
+;
+; Copyright (C) 2011-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+[Version]
+signature = "$Windows NT$"
+;cat CatalogFile = VBoxNetAdp.cat
+Class = Net
+ClassGUID = {4d36e972-e325-11ce-bfc1-08002be10318}
+Provider = %ORACLE%
+; DriverPackageType=Network
+; DriverPackageDisplayName=%VBoxNetAdp_Desc%
+;edit-DriverVer=08/13/2008,1.1.0.1
+
+[ControlFlags]
+;ExcludeFromSelect = sun_VBoxNetAdp
+
+[SourceDisksNames]
+1=%DiskDescription%,"",,
+
+[SourceDisksFiles]
+VBoxNetAdp.sys=1
+
+[DestinationDirs]
+DefaultDestDir = 12
+VBoxNetAdp.Files.Sys = 12 ; %windir%\System32\drivers
+
+[Manufacturer]
+%ORACLE% = VBoxNetAdp@COMMA-NT-ARCH@
+
+[VBoxNetAdp@DOT-NT-ARCH@]
+%VBoxNetAdp_Desc% = VBoxNetAdp.ndi, sun_VBoxNetAdp
+
+[VBoxNetAdp.ndi]
+Characteristics = 0x1 ; NCF_VIRTUAL
+CopyFiles = VBoxNetAdp.Files.Sys
+AddReg = VBoxNetAdp.AddReg
+
+[VBoxNetAdp.Files.Sys]
+VBoxNetAdp.sys,,,2
+
+[VBoxNetAdp.ndi.Services]
+AddService = VBoxNetAdp,0x2, VBoxNetAdp.AddService
+
+[VBoxNetAdp.AddService]
+DisplayName = %VBoxNetAdp_Desc%
+ServiceType = 1 ;SERVICE_KERNEL_DRIVER
+StartType = 3 ;SERVICE_DEMAND_START
+ErrorControl = 1 ;SERVICE_ERROR_NORMAL
+ServiceBinary = %12%\VBoxNetAdp.sys
+LoadOrderGroup = NDIS
+
+[VBoxNetAdp.AddReg]
+HKR, , *NdisDeviceType, 0x00010001, 1 ; NDIS_DEVICE_TYPE_ENDPOINT
+HKR, Ndi, Service, 0, "VBoxNetAdp"
+HKR, Ndi\Interfaces, UpperRange, 0, "ndis5"
+HKR, Ndi\Interfaces, LowerRange, 0, "ethernet"
+
+[Strings]
+ORACLE = "Oracle Corporation"
+VBoxNetAdp_Desc = "VirtualBox Host-Only Ethernet Adapter"
+DiskDescription = "VirtualBox Host-Only Ethernet Adapter"
+
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFlt-win.rc b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFlt-win.rc
new file mode 100644
index 00000000..979fb6dd
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFlt-win.rc
@@ -0,0 +1,77 @@
+/* $Id: VBoxNetFlt-win.rc $ */
+/** @file
+ * VBoxNetFlt - Resource file containing version info and icon.
+ */
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#include <windows.h>
+#include <VBox/version.h>
+
+#ifndef VBOXNETADP
+# define DESCRIPTION_STR "VirtualBox Bridged Networking Driver\0"
+# define FILENAME_STR "VBoxNetFlt"
+#else
+# define DESCRIPTION_STR "VirtualBox Host-Only Network Adapter Driver\0"
+# define FILENAME_STR "VBoxNetAdp"
+#endif
+
+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" // Lang=US English, CharSet=Unicode
+ BEGIN
+ VALUE "CompanyName", VBOX_RC_COMPANY_NAME
+ VALUE "FileDescription", DESCRIPTION_STR
+ VALUE "FileVersion", VBOX_RC_FILE_VERSION_STR
+ VALUE "InternalName", FILENAME_STR "\0"
+ VALUE "LegalCopyright", VBOX_RC_LEGAL_COPYRIGHT
+ VALUE "OriginalFilename", FILENAME_STR ".sys\0"
+ 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/VBoxNetFlt/win/drv/VBoxNetFlt.inf b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFlt.inf
new file mode 100644
index 00000000..22425793
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFlt.inf
@@ -0,0 +1,117 @@
+; $Id: VBoxNetFlt.inf $
+;; @file
+; VBoxNetFlt.inf - VirtualBox Bridged Networking Driver inf file Protocol edge
+;
+
+;
+; Copyright (C) 2011-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+[Version]
+Signature = "$Windows NT$"
+;cat CatalogFile = VBoxNetFlt.cat
+Class = NetService
+ClassGUID = {4D36E974-E325-11CE-BFC1-08002BE10318}
+Provider = %ORACLE%
+;DriverPackageType=Network
+;DriverPackageDisplayName=%VBoxNetFlt_Desc%
+;edit-DriverVer=08/13/2008,1.1.0.1
+
+
+[Manufacturer]
+%ORACLE% = VBoxNetFlt@COMMA-NT-ARCH@
+
+[ControlFlags]
+
+[VBoxNetFlt@DOT-NT-ARCH@]
+%VBoxNetFlt_Desc% = VBoxNetFlt.ndi, sun_VBoxNetFlt
+
+[VBoxNetFlt.ndi]
+AddReg = VBoxNetFlt.ndi.AddReg, VBoxNetFlt.AddReg
+Characteristics = 0x4410 ; NCF_FILTER | NCF_NDIS_PROTOCOL
+CopyFiles = VBoxNetFlt.Files.DLL, VBoxNetFlt.Files.Sys
+CopyInf = VBoxNetFltM.inf
+
+[VBoxNetFlt.ndi.Remove]
+DelFiles = VBoxNetFlt.Files.DLL, VBoxNetFlt.Files.Sys
+
+[VBoxNetFlt.ndi.Services]
+AddService = VBoxNetFlt,, VBoxNetFlt.AddService
+
+[VBoxNetFlt.AddService]
+DisplayName = %VBoxNetFltService_Desc%
+ServiceType = 1 ;SERVICE_KERNEL_DRIVER
+StartType = 3 ;SERVICE_DEMAND_START
+ErrorControl = 1 ;SERVICE_ERROR_NORMAL
+ServiceBinary = %12%\VBoxNetFlt.sys
+LoadOrderGroup = PNP_TDI
+AddReg = VBoxNetFlt.AddService.AddReg
+
+
+[VBoxNetFlt.AddService.AddReg]
+
+[SourceDisksNames]
+1=%DiskDescription%,"",,
+
+[SourceDisksFiles]
+VBoxNetFlt.sys=1
+VBoxNetFltNobj.dll=1
+
+[DestinationDirs]
+DefaultDestDir = 12
+VBoxNetFlt.Files.DLL = 11 ; %windir%\System32
+VBoxNetFlt.Files.Sys = 12 ; %windir%\System32\drivers
+
+[VBoxNetFlt.Files.Sys]
+VBoxNetFlt.sys,,,2
+
+[VBoxNetFlt.Files.DLL]
+VBoxNetFltNobj.dll,,,2
+
+[VBoxNetFlt.ndi.AddReg]
+HKR, Ndi, HelpText, , %VBoxNetFlt_HELP%
+HKR, Ndi, ClsID, 0, {f374d1a0-bf08-4bdc-9cb2-c15ddaeef955}
+HKR, Ndi, ComponentDll, , VBoxNetFltNobj.dll
+HKR, Ndi, FilterClass, , failover
+HKR, Ndi, FilterDeviceInfId, , sun_VBoxNetFltmp
+HKR, Ndi, Service, , VBoxNetFlt
+HKR, Ndi\Interfaces, UpperRange, , noupper
+HKR, Ndi\Interfaces, LowerRange, , nolower
+HKR, Ndi\Interfaces, FilterMediaTypes, , "ethernet, nolower"
+
+[VBoxNetFlt.AddReg]
+HKR, Parameters, Param1, 0, 4
+
+[Strings]
+ORACLE = "Oracle Corporation"
+DiskDescription = "VirtualBox Bridged Networking Driver"
+VBoxNetFlt_Desc = "VirtualBox Bridged Networking Driver"
+VBoxNetFlt_HELP = "VirtualBox Bridged Networking Driver"
+VBoxNetFltService_Desc = "VirtualBox Bridged Networking Service"
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltCmn-win.h b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltCmn-win.h
new file mode 100644
index 00000000..520a7d7c
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltCmn-win.h
@@ -0,0 +1,533 @@
+/* $Id: VBoxNetFltCmn-win.h $ */
+/** @file
+ * VBoxNetFltCmn-win.h - Bridged Networking Driver, Windows Specific Code.
+ * Common header with configuration defines and global defs
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_SRC_VBoxNetFlt_win_drv_VBoxNetFltCmn_win_h
+#define VBOX_INCLUDED_SRC_VBoxNetFlt_win_drv_VBoxNetFltCmn_win_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#define LOG_GROUP LOG_GROUP_NET_FLT_DRV
+
+/* debugging flags */
+#ifdef DEBUG
+//# define DEBUG_NETFLT_PACKETS
+# ifndef DEBUG_misha
+# define RT_NO_STRICT
+# endif
+/* # define DEBUG_NETFLT_LOOPBACK */
+/* receive logic has several branches */
+/* the DEBUG_NETFLT_RECV* macros used to debug the ProtocolReceive callback
+ * which is typically not used in case the underlying miniport indicates the packets with NdisMIndicateReceivePacket
+ * the best way to debug the ProtocolReceive (which in turn has several branches) is to enable the DEBUG_NETFLT_RECV
+ * one by one in the below order, i.e.
+ * first DEBUG_NETFLT_RECV
+ * then DEBUG_NETFLT_RECV + DEBUG_NETFLT_RECV_NOPACKET */
+//# define DEBUG_NETFLT_RECV
+//# define DEBUG_NETFLT_RECV_NOPACKET
+//# define DEBUG_NETFLT_RECV_TRANSFERDATA
+/* use ExAllocatePoolWithTag instead of NdisAllocateMemoryWithTag */
+// #define DEBUG_NETFLT_USE_EXALLOC
+#endif
+
+#include <VBox/intnet.h>
+#include <VBox/log.h>
+#include <VBox/err.h>
+#include <VBox/version.h>
+#include <iprt/initterm.h>
+#include <iprt/assert.h>
+#include <iprt/spinlock.h>
+#include <iprt/semaphore.h>
+#include <iprt/process.h>
+#include <iprt/alloc.h>
+#include <iprt/alloca.h>
+#include <iprt/time.h>
+#include <iprt/net.h>
+#include <iprt/list.h>
+
+#include <iprt/nt/ntddk.h>
+#include <iprt/nt/ndis.h>
+
+#define VBOXNETFLT_OS_SPECFIC 1
+
+/** version
+ * NOTE: we are NOT using NDIS 5.1 features now */
+#ifdef NDIS51_MINIPORT
+# define VBOXNETFLT_VERSION_MP_NDIS_MAJOR 5
+# define VBOXNETFLT_VERSION_MP_NDIS_MINOR 1
+#else
+# define VBOXNETFLT_VERSION_MP_NDIS_MAJOR 5
+# define VBOXNETFLT_VERSION_MP_NDIS_MINOR 0
+#endif
+
+#ifndef VBOXNETADP
+#ifdef NDIS51
+# define VBOXNETFLT_VERSION_PT_NDIS_MAJOR 5
+# define VBOXNETFLT_VERSION_PT_NDIS_MINOR 1 /* todo: use 0 here as well ? */
+#else
+# define VBOXNETFLT_VERSION_PT_NDIS_MAJOR 5
+# define VBOXNETFLT_VERSION_PT_NDIS_MINOR 0
+#endif
+
+# define VBOXNETFLT_NAME_PROTOCOL L"VBoxNetFlt"
+/** device to be used to prevent the driver unload & ioctl interface (if necessary in the future) */
+# define VBOXNETFLT_NAME_LINK L"\\DosDevices\\Global\\VBoxNetFlt"
+# define VBOXNETFLT_NAME_DEVICE L"\\Device\\VBoxNetFlt"
+#else
+# define VBOXNETFLT_NAME_LINK L"\\DosDevices\\Global\\VBoxNetAdp"
+# define VBOXNETFLT_NAME_DEVICE L"\\Device\\VBoxNetAdp"
+#endif
+
+typedef struct VBOXNETFLTINS *PVBOXNETFLTINS;
+
+/** configuration */
+
+/** Ndis Packet pool settings
+ * these are applied to both receive and send packet pools */
+/* number of packets for normal used */
+#define VBOXNETFLT_PACKET_POOL_SIZE_NORMAL 0x000000FF
+/* number of additional overflow packets */
+#define VBOXNETFLT_PACKET_POOL_SIZE_OVERFLOW 0x0000FF00
+
+/** packet queue size used when the driver is working in the "active" mode */
+#define VBOXNETFLT_PACKET_INFO_POOL_SIZE 0x0000FFFF
+
+#ifndef VBOXNETADP
+/** memory tag used for memory allocations
+ * (VBNF stands for VBox NetFlt) */
+# define VBOXNETFLT_MEM_TAG 'FNBV'
+#else
+/** memory tag used for memory allocations
+ * (VBNA stands for VBox NetAdp) */
+# define VBOXNETFLT_MEM_TAG 'ANBV'
+#endif
+
+/** receive and transmit Ndis buffer pool size */
+#define VBOXNETFLT_BUFFER_POOL_SIZE_TX 128
+#define VBOXNETFLT_BUFFER_POOL_SIZE_RX 128
+
+#define VBOXNETFLT_PACKET_ETHEADER_SIZE 14
+#define VBOXNETFLT_PACKET_HEADER_MATCH_SIZE 24
+#define VBOXNETFLT_PACKET_QUEUE_SG_SEGS_ALLOC 32
+
+
+#if defined(DEBUG_NETFLT_PACKETS) || !defined(VBOX_LOOPBACK_USEFLAGS)
+# define VBOXNETFLT_PACKETMATCH_LENGTH (VBOXNETFLT_PACKET_ETHEADER_SIZE + 2)
+#endif
+
+#ifdef VBOXNETADP
+#define VBOXNETADP_HEADER_SIZE 14
+#define VBOXNETADP_MAX_DATA_SIZE 1500
+#define VBOXNETADP_MAX_PACKET_SIZE (VBOXNETADP_HEADER_SIZE + VBOXNETADP_MAX_DATA_SIZE)
+#define VBOXNETADP_MIN_PACKET_SIZE 60
+/* link speed 100Mbps (measured in 100 bps) */
+#define VBOXNETADP_LINK_SPEED 1000000
+#define VBOXNETADP_MAX_LOOKAHEAD_SIZE VBOXNETADP_MAX_DATA_SIZE
+#define VBOXNETADP_VENDOR_ID 0x080027
+#define VBOXNETADP_VENDOR_DRIVER_VERSION 0x00010000
+#define VBOXNETADP_VENDOR_DESC "Sun"
+#define VBOXNETADP_MAX_MCAST_LIST 32
+#define VBOXNETADP_ETH_ADDRESS_LENGTH 6
+
+//#define VBOXNETADP_REPORT_DISCONNECTED
+#endif
+/* type defs */
+
+/** Flag specifying that the type of enqueued packet
+ * if set the info contains the PINTNETSG packet
+ * if clear the packet info contains the PNDIS_PACKET packet
+ * Typically the packet queue we are maintaining contains PNDIS_PACKETs only,
+ * however in case the underlying miniport indicates a packet with the NDIS_STATUS_RESOURCES status
+ * we MUST return the packet back to the miniport immediately
+ * this is why we are creating the INTNETSG, copying the ndis packet info there and enqueueing it */
+#define VBOXNETFLT_PACKET_SG 0x00000001
+
+/** the flag specifying that the packet source
+ * if set the packet comes from the host (upperlying protocol)
+ * if clear the packet comes from the wire (underlying miniport) */
+#define VBOXNETFLT_PACKET_SRC_HOST 0x00000002
+
+#ifndef VBOXNETFLT_NO_PACKET_QUEUE
+/** flag specifying the packet was originated by our driver
+ * i.e. we could use it on our needs and should not return it
+ * we are enqueueing "our" packets on ProtocolReceive call-back when
+ * Ndis does not give us a receive packet (the driver below us has called NdisM..IndicateReceive)
+ * this is supported for Ndis Packet only */
+#define VBOXNETFLT_PACKET_MINE 0x00000004
+
+/** flag passed to vboxNetFltWinQuEnqueuePacket specifying that the packet should be copied
+ * this is supported for Ndis Packet only */
+#define VBOXNETFLT_PACKET_COPY 0x00000008
+#endif
+
+/** packet queue element containing the packet info */
+typedef struct VBOXNETFLT_PACKET_INFO
+{
+ /** list entry used for enqueueing the info */
+ LIST_ENTRY ListEntry;
+ /** pointer to the pool containing this packet info */
+ struct VBOXNETFLT_PACKET_INFO_POOL *pPool;
+ /** flags describing the referenced packet. Contains PACKET_xxx flags (i.e. PACKET_SG, PACKET_SRC_HOST) */
+ uint32_t fFlags;
+ /** pointer to the packet this info represents */
+ PVOID pPacket;
+} VBOXNETFLT_PACKET_INFO, *PVBOXNETFLT_PACKET_INFO;
+
+/* paranoid check to make sure the elements in the packet info array are properly aligned */
+AssertCompile((sizeof(VBOXNETFLT_PACKET_INFO) & (sizeof(PVOID) - 1)) == 0);
+
+/** represents the packet queue */
+typedef LIST_ENTRY PVBOXNETFLT_ACKET_QUEUE, *PVBOXNETFLT_PACKET_QUEUE;
+
+/*
+ * we are using non-interlocked versions of LIST_ENTRY-related operations macros and synchronize
+ * access to the queue and its elements by acquiring/releasing a spinlock using Ndis[Acquire,Release]Spinlock
+ *
+ * we are NOT using interlocked versions of insert/remove head/tail list functions because we need to iterate though
+ * the queue elements as well as remove elements from the midle of the queue
+ *
+ * * @todo: it seems that we can switch to using interlocked versions of list-entry functions
+ * since we have removed all functionality (mentioned above, i.e. queue elements iteration, etc.) that might prevent us from doing this
+ */
+typedef struct VBOXNETFLT_INTERLOCKED_PACKET_QUEUE
+{
+ /** queue */
+ PVBOXNETFLT_ACKET_QUEUE Queue;
+ /** queue lock */
+ NDIS_SPIN_LOCK Lock;
+} VBOXNETFLT_INTERLOCKED_PACKET_QUEUE, *PVBOXNETFLT_INTERLOCKED_PACKET_QUEUE;
+
+typedef struct VBOXNETFLT_SINGLE_LIST
+{
+ /** queue */
+ SINGLE_LIST_ENTRY Head;
+ /** pointer to the list tail. used to enqueue elements to the tail of the list */
+ PSINGLE_LIST_ENTRY pTail;
+} VBOXNETFLT_SINGLE_LIST, *PVBOXNETFLT_SINGLE_LIST;
+
+typedef struct VBOXNETFLT_INTERLOCKED_SINGLE_LIST
+{
+ /** queue */
+ VBOXNETFLT_SINGLE_LIST List;
+ /** queue lock */
+ NDIS_SPIN_LOCK Lock;
+} VBOXNETFLT_INTERLOCKED_SINGLE_LIST, *PVBOXNETFLT_INTERLOCKED_SINGLE_LIST;
+
+/** packet info pool contains free packet info elements to be used for the packet queue
+ * we are using the pool mechanism to allocate packet queue elements
+ * the pool mechanism is pretty simple now, we are allocating a bunch of memory
+ * for maintaining VBOXNETFLT_PACKET_INFO_POOL_SIZE queue elements and just returning null when the pool is exhausted
+ * This mechanism seems to be enough for now since we are using VBOXNETFLT_PACKET_INFO_POOL_SIZE = 0xffff which is
+ * the maximum size of packets the ndis packet pool supports */
+typedef struct VBOXNETFLT_PACKET_INFO_POOL
+{
+ /** free packet info queue */
+ VBOXNETFLT_INTERLOCKED_PACKET_QUEUE Queue;
+ /** memory bugger used by the pool */
+ PVOID pBuffer;
+} VBOXNETFLT_PACKET_INFO_POOL, *PVBOXNETFLT_PACKET_INFO_POOL;
+
+typedef enum VBOXNETDEVOPSTATE
+{
+ kVBoxNetDevOpState_InvalidValue = 0,
+ kVBoxNetDevOpState_Initializing,
+ kVBoxNetDevOpState_Initialized,
+ kVBoxNetDevOpState_Deinitializing,
+ kVBoxNetDevOpState_Deinitialized,
+
+} VBOXNETDEVOPSTATE;
+
+typedef enum VBOXNETFLT_WINIFSTATE
+{
+ /** The usual invalid state. */
+ kVBoxWinIfState_Invalid = 0,
+ /** Initialization. */
+ kVBoxWinIfState_Connecting,
+ /** Connected fuly functional state */
+ kVBoxWinIfState_Connected,
+ /** Disconnecting */
+ kVBoxWinIfState_Disconnecting,
+ /** Disconnected */
+ kVBoxWinIfState_Disconnected,
+} VBOXNETFLT_WINIFSTATE;
+
+/** structure used to maintain the state and reference count of the miniport and protocol */
+typedef struct VBOXNETFLT_WINIF_DEVICE
+{
+ /** initialize state */
+ VBOXNETDEVOPSTATE OpState;
+ /** ndis power state */
+ NDIS_DEVICE_POWER_STATE PowerState;
+ /** reference count */
+ uint32_t cReferences;
+} VBOXNETFLT_WINIF_DEVICE, *PVBOXNETFLT_WINIF_DEVICE;
+
+#define VBOXNDISREQUEST_INPROGRESS 1
+#define VBOXNDISREQUEST_QUEUED 2
+
+typedef struct VBOXNETFLTWIN_STATE
+{
+ union
+ {
+ struct
+ {
+ UINT fRequestInfo : 2;
+ UINT fInterfaceClosing : 1;
+ UINT fStandBy : 1;
+ UINT fProcessingPacketFilter : 1;
+ UINT fPPFNetFlt : 1;
+ UINT fUpperProtSetFilterInitialized : 1;
+ UINT Reserved : 25;
+ };
+ UINT Value;
+ };
+} VBOXNETFLTWIN_STATE, *PVBOXNETFLTWIN_STATE;
+
+DECLINLINE(VBOXNETFLTWIN_STATE) vboxNetFltWinAtomicUoReadWinState(VBOXNETFLTWIN_STATE State)
+{
+ UINT fValue = ASMAtomicUoReadU32((volatile uint32_t *)&State.Value);
+ return *((PVBOXNETFLTWIN_STATE)((void*)&fValue));
+}
+
+/* miniport layer globals */
+typedef struct VBOXNETFLTGLOBALS_MP
+{
+ /** our miniport handle */
+ NDIS_HANDLE hMiniport;
+ /** ddis wrapper handle */
+ NDIS_HANDLE hNdisWrapper;
+} VBOXNETFLTGLOBALS_MP, *PVBOXNETFLTGLOBALS_MP;
+
+#ifndef VBOXNETADP
+/* protocol layer globals */
+typedef struct VBOXNETFLTGLOBALS_PT
+{
+ /** our protocol handle */
+ NDIS_HANDLE hProtocol;
+} VBOXNETFLTGLOBALS_PT, *PVBOXNETFLTGLOBALS_PT;
+#endif /* #ifndef VBOXNETADP */
+
+typedef struct VBOXNETFLTGLOBALS_WIN
+{
+ /** synch event used for device creation synchronization */
+ KEVENT SynchEvent;
+ /** Device reference count */
+ int cDeviceRefs;
+ /** ndis device */
+ NDIS_HANDLE hDevice;
+ /** device object */
+ PDEVICE_OBJECT pDevObj;
+ /* loopback flags */
+ /* ndis packet flags to disable packet loopback */
+ UINT fPacketDontLoopBack;
+ /* ndis packet flags specifying whether the packet is looped back */
+ UINT fPacketIsLoopedBack;
+ /* Minport info */
+ VBOXNETFLTGLOBALS_MP Mp;
+#ifndef VBOXNETADP
+ /* Protocol info */
+ VBOXNETFLTGLOBALS_PT Pt;
+ /** lock protecting the filter list */
+ NDIS_SPIN_LOCK lockFilters;
+ /** the head of filter list */
+ RTLISTANCHOR listFilters;
+ /** IP address change notifier handle */
+ HANDLE hNotifier;
+#endif
+} VBOXNETFLTGLOBALS_WIN, *PVBOXNETFLTGLOBALS_WIN;
+
+extern VBOXNETFLTGLOBALS_WIN g_VBoxNetFltGlobalsWin;
+
+/** represents filter driver device context*/
+typedef struct VBOXNETFLTWIN
+{
+ /** handle used by miniport edge for ndis calls */
+ NDIS_HANDLE hMiniport;
+ /** miniport edge state */
+ VBOXNETFLT_WINIF_DEVICE MpState;
+ /** ndis packet pool used for receives */
+ NDIS_HANDLE hRecvPacketPool;
+ /** ndis buffer pool used for receives */
+ NDIS_HANDLE hRecvBufferPool;
+ /** driver bind adapter state. */
+ VBOXNETFLT_WINIFSTATE enmState;
+#ifndef VBOXNETADP
+ /* misc state flags */
+ VBOXNETFLTWIN_STATE StateFlags;
+ /** handle used by protocol edge for ndis calls */
+ NDIS_HANDLE hBinding;
+ /** protocol edge state */
+ VBOXNETFLT_WINIF_DEVICE PtState;
+ /** ndis packet pool used for receives */
+ NDIS_HANDLE hSendPacketPool;
+ /** ndis buffer pool used for receives */
+ NDIS_HANDLE hSendBufferPool;
+ /** used for maintaining the pending send packets for handling packet loopback */
+ VBOXNETFLT_INTERLOCKED_SINGLE_LIST SendPacketQueue;
+ /** used for serializing calls to the NdisRequest in the vboxNetFltWinSynchNdisRequest */
+ RTSEMFASTMUTEX hSynchRequestMutex;
+ /** event used to synchronize with the Ndis Request completion in the vboxNetFltWinSynchNdisRequest */
+ KEVENT hSynchCompletionEvent;
+ /** status of the Ndis Request initiated by the vboxNetFltWinSynchNdisRequest */
+ NDIS_STATUS volatile SynchCompletionStatus;
+ /** pointer to the Ndis Request being executed by the vboxNetFltWinSynchNdisRequest */
+ PNDIS_REQUEST volatile pSynchRequest;
+ /** open/close adapter status.
+ * Since ndis adapter open and close requests may complete asynchronously,
+ * we are using event mechanism to wait for open/close completion
+ * the status field is being set by the completion call-back */
+ NDIS_STATUS OpenCloseStatus;
+ /** open/close adaptor completion event */
+ NDIS_EVENT OpenCloseEvent;
+ /** medium we are attached to */
+ NDIS_MEDIUM enmMedium;
+ /**
+ * Passdown request info
+ */
+ /** ndis request we pass down to the miniport below */
+ NDIS_REQUEST PassDownRequest;
+ /** Ndis pass down request bytes read or written original pointer */
+ PULONG pcPDRBytesRW;
+ /** Ndis pass down request bytes needed original pointer */
+ PULONG pcPDRBytesNeeded;
+ /** true if we should indicate the receive complete used by the ProtocolReceive mechanism.
+ * We need to indicate it only with the ProtocolReceive + NdisMEthIndicateReceive path.
+ * Note: we're using KeGetCurrentProcessorNumber, which is not entirely correct in case
+ * we're running on 64bit win7+, which can handle > 64 CPUs, however since KeGetCurrentProcessorNumber
+ * always returns the number < than the number of CPUs in the first group, we're guaranteed to have CPU index < 64
+ * @todo: use KeGetCurrentProcessorNumberEx for Win7+ 64 and dynamically extended array */
+ bool abIndicateRxComplete[64];
+ /** Pending transfer data packet queue (i.e. packets that were indicated as pending on NdisTransferData call */
+ VBOXNETFLT_INTERLOCKED_SINGLE_LIST TransferDataList;
+ /* mac options initialized on OID_GEN_MAC_OPTIONS */
+ ULONG fMacOptions;
+ /** our miniport devuice name */
+ NDIS_STRING MpDeviceName;
+ /** synchronize with unbind with Miniport initialization */
+ NDIS_EVENT MpInitCompleteEvent;
+ /** media connect status that we indicated */
+ NDIS_STATUS MpIndicatedMediaStatus;
+ /** media connect status pending to indicate */
+ NDIS_STATUS MpUnindicatedMediaStatus;
+ /** packet filter flags set by the upper protocols */
+ ULONG fUpperProtocolSetFilter;
+ /** packet filter flags set by the upper protocols */
+ ULONG fSetFilterBuffer;
+ /** packet filter flags set by us */
+ ULONG fOurSetFilter;
+ /** our own list of filters, needed by notifier */
+ RTLISTNODE node;
+#else
+ volatile ULONG cTxSuccess;
+ volatile ULONG cRxSuccess;
+ volatile ULONG cTxError;
+ volatile ULONG cRxError;
+#endif
+} VBOXNETFLTWIN, *PVBOXNETFLTWIN;
+
+typedef struct VBOXNETFLT_PACKET_QUEUE_WORKER
+{
+ /** this event is used to initiate a packet queue worker thread kill */
+ KEVENT KillEvent;
+ /** this event is used to notify a worker thread that the packets are added to the queue */
+ KEVENT NotifyEvent;
+ /** pointer to the packet queue worker thread object */
+ PKTHREAD pThread;
+ /** pointer to the SG used by the packet queue for IntNet receive notifications */
+ PINTNETSG pSG;
+ /** Packet queue */
+ VBOXNETFLT_INTERLOCKED_PACKET_QUEUE PacketQueue;
+ /** Packet info pool, i.e. the pool for the packet queue elements */
+ VBOXNETFLT_PACKET_INFO_POOL PacketInfoPool;
+} VBOXNETFLT_PACKET_QUEUE_WORKER, *PVBOXNETFLT_PACKET_QUEUE_WORKER;
+
+/* protocol reserved data held in ndis packet */
+typedef struct VBOXNETFLT_PKTRSVD_PT
+{
+ /** original packet received from the upperlying protocol
+ * can be null if the packet was originated by intnet */
+ PNDIS_PACKET pOrigPacket;
+ /** pointer to the buffer to be freed on send completion
+ * can be null if no buffer is to be freed */
+ PVOID pBufToFree;
+#if !defined(VBOX_LOOPBACK_USEFLAGS) || defined(DEBUG_NETFLT_PACKETS)
+ SINGLE_LIST_ENTRY ListEntry;
+ /* true if the packet is from IntNet */
+ bool bFromIntNet;
+#endif
+} VBOXNETFLT_PKTRSVD_PT, *PVBOXNETFLT_PKTRSVD_PT;
+
+/** miniport reserved data held in ndis packet */
+typedef struct VBOXNETFLT_PKTRSVD_MP
+{
+ /** original packet received from the underling miniport
+ * can be null if the packet was originated by intnet */
+ PNDIS_PACKET pOrigPacket;
+ /** pointer to the buffer to be freed on receive completion
+ * can be null if no buffer is to be freed */
+ PVOID pBufToFree;
+} VBOXNETFLT_PKTRSVD_MP, *PVBOXNETFLT_PKTRSVD_MP;
+
+/** represents the data stored in the protocol reserved field of ndis packet on NdisTransferData processing */
+typedef struct VBOXNETFLT_PKTRSVD_TRANSFERDATA_PT
+{
+ /** next packet in a list */
+ SINGLE_LIST_ENTRY ListEntry;
+ /* packet buffer start */
+ PNDIS_BUFFER pOrigBuffer;
+} VBOXNETFLT_PKTRSVD_TRANSFERDATA_PT, *PVBOXNETFLT_PKTRSVD_TRANSFERDATA_PT;
+
+/* VBOXNETFLT_PKTRSVD_TRANSFERDATA_PT should fit into PROTOCOL_RESERVED_SIZE_IN_PACKET because we use protocol reserved part
+ * of our miniport edge on transfer data processing for honding our own info */
+AssertCompile(sizeof (VBOXNETFLT_PKTRSVD_TRANSFERDATA_PT) <= PROTOCOL_RESERVED_SIZE_IN_PACKET);
+/* this should fit in MiniportReserved */
+AssertCompile(sizeof (VBOXNETFLT_PKTRSVD_MP) <= RT_SIZEOFMEMB(NDIS_PACKET, MiniportReserved));
+/* we use RTAsmAtomic*U32 for those, make sure we're correct */
+AssertCompile(sizeof (NDIS_DEVICE_POWER_STATE) == sizeof (uint32_t));
+AssertCompile(sizeof (UINT) == sizeof (uint32_t));
+
+
+#define NDIS_FLAGS_SKIP_LOOPBACK_W2K 0x400
+
+#include "../../VBoxNetFltInternal.h"
+#include "VBoxNetFltRt-win.h"
+#ifndef VBOXNETADP
+# include "VBoxNetFltP-win.h"
+#endif
+#include "VBoxNetFltM-win.h"
+
+#endif /* !VBOX_INCLUDED_SRC_VBoxNetFlt_win_drv_VBoxNetFltCmn_win_h */
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltM-win.cpp b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltM-win.cpp
new file mode 100644
index 00000000..65bde454
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltM-win.cpp
@@ -0,0 +1,1570 @@
+/* $Id: VBoxNetFltM-win.cpp $ */
+/** @file
+ * VBoxNetFltM-win.cpp - Bridged Networking Driver, Windows Specific Code.
+ * Miniport edge
+ */
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+#include "VBoxNetFltCmn-win.h"
+
+static const char* vboxNetFltWinMpDumpOid(ULONG oid);
+
+#ifndef VBOXNETADP
+static NDIS_STATUS vboxNetFltWinMpInitialize(OUT PNDIS_STATUS OpenErrorStatus,
+ OUT PUINT SelectedMediumIndex,
+ IN PNDIS_MEDIUM MediumArray,
+ IN UINT MediumArraySize,
+ IN NDIS_HANDLE MiniportAdapterHandle,
+ IN NDIS_HANDLE WrapperConfigurationContext)
+{
+ RT_NOREF1(WrapperConfigurationContext);
+ PVBOXNETFLTINS pNetFlt = (PVBOXNETFLTINS)NdisIMGetDeviceContext(MiniportAdapterHandle);
+ NDIS_STATUS Status = NDIS_STATUS_FAILURE;
+
+ LogFlowFunc(("ENTER: pNetFlt (0x%p)\n", pNetFlt));
+
+ pNetFlt->u.s.WinIf.hMiniport = MiniportAdapterHandle;
+ Assert(vboxNetFltWinGetOpState(&pNetFlt->u.s.WinIf.MpState) == kVBoxNetDevOpState_Initializing);
+ /* the MP state should be already set to kVBoxNetDevOpState_Initializing, just a paranoia
+ * in case NDIS for some reason calls us in some irregular way */
+ vboxNetFltWinSetOpState(&pNetFlt->u.s.WinIf.MpState, kVBoxNetDevOpState_Initializing);
+
+ NDIS_MEDIUM enmMedium = pNetFlt->u.s.WinIf.enmMedium;
+ if (enmMedium == NdisMediumWan)
+ enmMedium = NdisMedium802_3;
+
+ UINT i = 0;
+ for (; i < MediumArraySize; i++)
+ {
+ if (MediumArray[i] == enmMedium)
+ {
+ *SelectedMediumIndex = i;
+ break;
+ }
+ }
+
+ do
+ {
+ if (i != MediumArraySize)
+ {
+ NdisMSetAttributesEx(MiniportAdapterHandle, pNetFlt, 0,
+ NDIS_ATTRIBUTE_IGNORE_PACKET_TIMEOUT |
+ NDIS_ATTRIBUTE_IGNORE_REQUEST_TIMEOUT|
+ NDIS_ATTRIBUTE_INTERMEDIATE_DRIVER |
+ NDIS_ATTRIBUTE_DESERIALIZE |
+ NDIS_ATTRIBUTE_NO_HALT_ON_SUSPEND,
+ NdisInterfaceInternal /* 0 */);
+
+ pNetFlt->u.s.WinIf.MpIndicatedMediaStatus = NDIS_STATUS_MEDIA_CONNECT;
+ Assert(vboxNetFltWinGetPowerState(&pNetFlt->u.s.WinIf.MpState) == NdisDeviceStateD3);
+ vboxNetFltWinSetPowerState(&pNetFlt->u.s.WinIf.MpState, NdisDeviceStateD0);
+ Assert(pNetFlt->u.s.WinIf.MpState.OpState == kVBoxNetDevOpState_Initializing);
+ vboxNetFltWinSetOpState(&pNetFlt->u.s.WinIf.MpState, kVBoxNetDevOpState_Initialized);
+
+ Status = NDIS_STATUS_SUCCESS;
+ break;
+ }
+ else
+ {
+ Status = NDIS_STATUS_UNSUPPORTED_MEDIA;
+ }
+
+ Assert(Status != NDIS_STATUS_SUCCESS);
+ Assert(vboxNetFltWinGetPowerState(&pNetFlt->u.s.WinIf.MpState) == NdisDeviceStateD3);
+ Assert(pNetFlt->u.s.WinIf.MpState.OpState == kVBoxNetDevOpState_Initializing);
+ vboxNetFltWinSetOpState(&pNetFlt->u.s.WinIf.MpState, kVBoxNetDevOpState_Deinitialized);
+ } while (0);
+
+ NdisSetEvent(&pNetFlt->u.s.WinIf.MpInitCompleteEvent);
+
+ LogFlowFunc(("LEAVE: pNetFlt (0x%p), Status (0x%x)\n", pNetFlt, Status));
+
+ *OpenErrorStatus = Status;
+
+ return Status;
+}
+
+/**
+ * process the packet send in a "passthru" mode
+ */
+static NDIS_STATUS vboxNetFltWinSendPassThru(PVBOXNETFLTINS pNetFlt, PNDIS_PACKET pPacket
+#ifdef VBOXNETFLT_NO_PACKET_QUEUE
+ , bool bNetFltActive
+#endif
+ )
+{
+ PNDIS_PACKET pMyPacket;
+ NDIS_STATUS Status = vboxNetFltWinPrepareSendPacket(pNetFlt, pPacket, &pMyPacket);
+ Assert(Status == NDIS_STATUS_SUCCESS);
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+#if !defined(VBOX_LOOPBACK_USEFLAGS) /* || defined(DEBUG_NETFLT_PACKETS) */
+# ifdef VBOXNETFLT_NO_PACKET_QUEUE
+ if (bNetFltActive)
+ vboxNetFltWinLbPutSendPacket(pNetFlt, pMyPacket, false /* bFromIntNet */);
+# else
+ /* no need for the loop enqueue & check in a passthru mode , ndis will do everything for us */
+# endif
+#endif
+ NdisSend(&Status, pNetFlt->u.s.WinIf.hBinding, pMyPacket);
+ if (Status != NDIS_STATUS_PENDING)
+ {
+ NdisIMCopySendCompletePerPacketInfo(pPacket, pMyPacket);
+#if defined(VBOXNETFLT_NO_PACKET_QUEUE) && !defined(VBOX_LOOPBACK_USEFLAGS)
+ if (bNetFltActive)
+ vboxNetFltWinLbRemoveSendPacket(pNetFlt, pMyPacket);
+#endif
+ NdisFreePacket(pMyPacket);
+ }
+ }
+ return Status;
+}
+
+#else /* defined VBOXNETADP */
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinMpDoDeinitialization(PVBOXNETFLTINS pNetFlt)
+{
+ uint64_t NanoTS = RTTimeSystemNanoTS();
+
+ Assert(vboxNetFltWinGetOpState(&pNetFlt->u.s.WinIf.MpState) == kVBoxNetDevOpState_Initialized);
+
+ RTSpinlockAcquire(pNetFlt->hSpinlock);
+ ASMAtomicUoWriteBool(&pNetFlt->fDisconnectedFromHost, true);
+ ASMAtomicUoWriteBool(&pNetFlt->fRediscoveryPending, false);
+ ASMAtomicUoWriteU64(&pNetFlt->NanoTSLastRediscovery, NanoTS);
+
+ vboxNetFltWinSetOpState(&pNetFlt->u.s.WinIf.MpState, kVBoxNetDevOpState_Deinitializing);
+
+ RTSpinlockRelease(pNetFlt->hSpinlock);
+
+ vboxNetFltWinWaitDereference(&pNetFlt->u.s.WinIf.MpState);
+
+ /* check packet pool is empty */
+ int cPPUsage = NdisPacketPoolUsage(pNetFlt->u.s.WinIf.hRecvPacketPool);
+ Assert(cPPUsage == 0);
+ /* for debugging only, ignore the err in release */
+ NOREF(cPPUsage);
+
+ vboxNetFltWinSetOpState(&pNetFlt->u.s.WinIf.MpState, kVBoxNetDevOpState_Deinitialized);
+
+ return NDIS_STATUS_SUCCESS;
+}
+
+static NDIS_STATUS vboxNetFltWinMpReadApplyConfig(PVBOXNETFLTINS pThis, NDIS_HANDLE hMiniportAdapter,
+ NDIS_HANDLE hWrapperConfigurationContext)
+{
+ RT_NOREF1(hMiniportAdapter);
+ NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
+ NDIS_HANDLE hConfiguration;
+ PNDIS_CONFIGURATION_PARAMETER pParameterValue;
+ NDIS_STRING strMAC = NDIS_STRING_CONST("MAC");
+ RTMAC mac;
+
+ NdisOpenConfiguration(
+ &Status,
+ &hConfiguration,
+ hWrapperConfigurationContext);
+ Assert(Status == NDIS_STATUS_SUCCESS);
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ do
+ {
+ int rc;
+ NDIS_CONFIGURATION_PARAMETER param;
+ WCHAR MacBuf[13];
+
+ NdisReadConfiguration(&Status,
+ &pParameterValue,
+ hConfiguration,
+ &strMAC,
+ NdisParameterString);
+// Assert(Status == NDIS_STATUS_SUCCESS);
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+
+ rc = vboxNetFltWinMACFromNdisString(&mac, &pParameterValue->ParameterData.StringData);
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ break;
+ }
+ }
+
+ vboxNetFltWinGenerateMACAddress(&mac);
+ param.ParameterType = NdisParameterString;
+ param.ParameterData.StringData.Buffer = MacBuf;
+ param.ParameterData.StringData.MaximumLength = sizeof(MacBuf);
+
+ rc = vboxNetFltWinMAC2NdisString(&mac, &param.ParameterData.StringData);
+ Assert(RT_SUCCESS(rc));
+ if (RT_SUCCESS(rc))
+ {
+ NdisWriteConfiguration(&Status,
+ hConfiguration,
+ &strMAC,
+ &param);
+ Assert(Status == NDIS_STATUS_SUCCESS);
+ if (Status != NDIS_STATUS_SUCCESS)
+ {
+ /* ignore the failure */
+ Status = NDIS_STATUS_SUCCESS;
+ }
+ }
+ } while (0);
+
+ NdisCloseConfiguration(hConfiguration);
+ }
+ else
+ {
+ vboxNetFltWinGenerateMACAddress(&mac);
+ }
+
+ pThis->u.s.MacAddr = mac;
+
+ return NDIS_STATUS_SUCCESS;
+}
+
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinMpDoInitialization(PVBOXNETFLTINS pNetFlt, NDIS_HANDLE hMiniportAdapter, NDIS_HANDLE hWrapperConfigurationContext)
+{
+ NDIS_STATUS Status;
+ pNetFlt->u.s.WinIf.hMiniport = hMiniportAdapter;
+
+ LogFlowFunc(("ENTER: pNetFlt 0x%p\n", pNetFlt));
+
+ Assert(vboxNetFltWinGetOpState(&pNetFlt->u.s.WinIf.MpState) == kVBoxNetDevOpState_Deinitialized);
+ vboxNetFltWinSetOpState(&pNetFlt->u.s.WinIf.MpState, kVBoxNetDevOpState_Initializing);
+
+ vboxNetFltWinMpReadApplyConfig(pNetFlt, hMiniportAdapter, hWrapperConfigurationContext);
+
+ NdisMSetAttributesEx(hMiniportAdapter, pNetFlt,
+ 0, /* CheckForHangTimeInSeconds */
+ NDIS_ATTRIBUTE_DESERIALIZE |
+ NDIS_ATTRIBUTE_NO_HALT_ON_SUSPEND,
+ NdisInterfaceInternal/* 0 */);
+
+ Assert(vboxNetFltWinGetPowerState(&pNetFlt->u.s.WinIf.MpState) == NdisDeviceStateD3);
+ vboxNetFltWinSetPowerState(&pNetFlt->u.s.WinIf.MpState, NdisDeviceStateD0);
+ Assert(vboxNetFltWinGetOpState(&pNetFlt->u.s.WinIf.MpState) == kVBoxNetDevOpState_Initializing);
+ vboxNetFltWinSetOpState(&pNetFlt->u.s.WinIf.MpState, kVBoxNetDevOpState_Initialized);
+
+ Status = NDIS_STATUS_SUCCESS;
+
+ LogFlowFunc(("pNetFlt 0x%p, Status 0x%x\n", pNetFlt, Status));
+
+ return Status;
+}
+
+static NDIS_STATUS vboxNetFltWinMpInitialize(OUT PNDIS_STATUS OpenErrorStatus,
+ OUT PUINT SelectedMediumIndex,
+ IN PNDIS_MEDIUM MediumArray,
+ IN UINT MediumArraySize,
+ IN NDIS_HANDLE MiniportAdapterHandle,
+ IN NDIS_HANDLE WrapperConfigurationContext)
+{
+
+ NDIS_STATUS Status = NDIS_STATUS_FAILURE;
+ UINT i = 0;
+
+ LogFlowFuncEnter();
+
+ for (; i < MediumArraySize; i++)
+ {
+ if (MediumArray[i] == NdisMedium802_3)
+ {
+ *SelectedMediumIndex = i;
+ break;
+ }
+ }
+
+ if (i != MediumArraySize)
+ {
+ PDEVICE_OBJECT pPdo, pFdo;
+#define KEY_PREFIX L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Class\\"
+ UCHAR Buf[512];
+ PUCHAR pSuffix;
+ ULONG cbBuf;
+ NDIS_STRING RtlStr;
+
+ wcscpy((WCHAR*)Buf, KEY_PREFIX);
+ pSuffix = Buf + (sizeof(KEY_PREFIX)-2);
+
+ NdisMGetDeviceProperty(MiniportAdapterHandle,
+ &pPdo,
+ &pFdo,
+ NULL, //Next Device Object
+ NULL,
+ NULL);
+
+ Status = IoGetDeviceProperty (pPdo,
+ DevicePropertyDriverKeyName,
+ sizeof(Buf) - (sizeof(KEY_PREFIX)-2),
+ pSuffix,
+ &cbBuf);
+ if (Status == STATUS_SUCCESS)
+ {
+ OBJECT_ATTRIBUTES ObjAttr;
+ HANDLE hDrvKey;
+ RtlStr.Buffer=(WCHAR*)Buf;
+ RtlStr.Length=(USHORT)cbBuf - 2 + sizeof(KEY_PREFIX) - 2;
+ RtlStr.MaximumLength=sizeof(Buf);
+
+ InitializeObjectAttributes(&ObjAttr, &RtlStr, OBJ_CASE_INSENSITIVE, NULL, NULL);
+
+ Status = ZwOpenKey(&hDrvKey, KEY_READ, &ObjAttr);
+ if (Status == STATUS_SUCCESS)
+ {
+ static UNICODE_STRING NetCfgInstanceIdValue = NDIS_STRING_CONST("NetCfgInstanceId");
+// UCHAR valBuf[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + RTUUID_STR_LENGTH*2 + 10];
+// ULONG cLength = sizeof(valBuf);
+#define NAME_PREFIX L"\\DEVICE\\"
+ PKEY_VALUE_PARTIAL_INFORMATION pInfo = (PKEY_VALUE_PARTIAL_INFORMATION)Buf;
+ Status = ZwQueryValueKey(hDrvKey,
+ &NetCfgInstanceIdValue,
+ KeyValuePartialInformation,
+ pInfo,
+ sizeof(Buf),
+ &cbBuf);
+ if (Status == STATUS_SUCCESS)
+ {
+ if (pInfo->Type == REG_SZ && pInfo->DataLength > 2)
+ {
+ WCHAR *pName;
+ Status = vboxNetFltWinMemAlloc((PVOID*)&pName, pInfo->DataLength + sizeof(NAME_PREFIX));
+ if (Status == STATUS_SUCCESS)
+ {
+ PVBOXNETFLTINS pNetFlt;
+ wcscpy(pName, NAME_PREFIX);
+ wcscpy(pName+(sizeof(NAME_PREFIX)-2)/2, (WCHAR*)pInfo->Data);
+ RtlStr.Buffer=pName;
+ RtlStr.Length = (USHORT)pInfo->DataLength - 2 + sizeof(NAME_PREFIX) - 2;
+ RtlStr.MaximumLength = (USHORT)pInfo->DataLength + sizeof(NAME_PREFIX);
+
+ Status = vboxNetFltWinPtInitBind(&pNetFlt, MiniportAdapterHandle, &RtlStr, WrapperConfigurationContext);
+
+ if (Status == STATUS_SUCCESS)
+ {
+ Assert(vboxNetFltWinGetOpState(&pNetFlt->u.s.WinIf.MpState) == kVBoxNetDevOpState_Initialized);
+ vboxNetFltWinSetOpState(&pNetFlt->u.s.WinIf.MpState, kVBoxNetDevOpState_Initialized);
+#if 0
+ NdisMIndicateStatus(pNetFlt->u.s.WinIf.hMiniport,
+ NDIS_STATUS_MEDIA_CONNECT,
+ (PVOID)NULL,
+ 0);
+#endif
+ }
+ else
+ {
+ Assert(vboxNetFltWinGetOpState(&pNetFlt->u.s.WinIf.MpState) == kVBoxNetDevOpState_Deinitialized);
+ vboxNetFltWinSetOpState(&pNetFlt->u.s.WinIf.MpState, kVBoxNetDevOpState_Deinitialized);
+ }
+
+ vboxNetFltWinMemFree(pName);
+
+ }
+ }
+ else
+ {
+ Status = NDIS_STATUS_FAILURE;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ Status = NDIS_STATUS_UNSUPPORTED_MEDIA;
+ }
+
+ /** @todo */
+ *OpenErrorStatus = Status;
+
+ LogFlowFunc(("LEAVE: Status (0x%x)\n", Status));
+
+ return Status;
+}
+#endif
+
+static VOID vboxNetFltWinMpSendPackets(IN NDIS_HANDLE hMiniportAdapterContext,
+ IN PPNDIS_PACKET pPacketArray,
+ IN UINT cNumberOfPackets)
+{
+ PVBOXNETFLTINS pNetFlt = (PVBOXNETFLTINS)hMiniportAdapterContext;
+ NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
+ bool bNetFltActive;
+
+ LogFlowFunc(("ENTER: pNetFlt (0x%p)\n", pNetFlt));
+
+ Assert(cNumberOfPackets);
+
+ if (vboxNetFltWinIncReferenceWinIfNetFlt(pNetFlt, cNumberOfPackets, &bNetFltActive))
+ {
+ uint32_t cAdaptRefs = cNumberOfPackets;
+ uint32_t cNetFltRefs;
+ uint32_t cPassThruRefs;
+ if (bNetFltActive)
+ {
+ cNetFltRefs = cNumberOfPackets;
+ cPassThruRefs = 0;
+ }
+ else
+ {
+ cPassThruRefs = cNumberOfPackets;
+ cNetFltRefs = 0;
+ }
+
+ for (UINT i = 0; i < cNumberOfPackets; i++)
+ {
+ PNDIS_PACKET pPacket;
+
+ pPacket = pPacketArray[i];
+
+ if (!cNetFltRefs
+#ifdef VBOXNETFLT_NO_PACKET_QUEUE
+ || !vboxNetFltWinPostIntnet(pNetFlt, pPacket, VBOXNETFLT_PACKET_SRC_HOST)
+#else
+ || (fStatus = vboxNetFltWinQuEnqueuePacket(pNetFlt, pPacket, VBOXNETFLT_PACKET_SRC_HOST)) != NDIS_STATUS_SUCCESS
+#endif
+ )
+ {
+#ifndef VBOXNETADP
+ Status = vboxNetFltWinSendPassThru(pNetFlt, pPacket
+#ifdef VBOXNETFLT_NO_PACKET_QUEUE
+ , !!cNetFltRefs
+#endif
+ );
+#else
+ if (!cNetFltRefs)
+ {
+# ifdef VBOXNETADP_REPORT_DISCONNECTED
+ Status = NDIS_STATUS_MEDIA_DISCONNECT;
+ STATISTIC_INCREASE(pNetFlt->u.s.WinIf.cTxError);
+# else
+ Status = NDIS_STATUS_SUCCESS;
+# endif
+ }
+#endif
+
+ if (Status != NDIS_STATUS_PENDING)
+ {
+ NdisMSendComplete(pNetFlt->u.s.WinIf.hMiniport, pPacket, Status);
+ }
+ else
+ {
+ cAdaptRefs--;
+ }
+ }
+ else
+ {
+#ifdef VBOXNETFLT_NO_PACKET_QUEUE
+ NdisMSendComplete(pNetFlt->u.s.WinIf.hMiniport, pPacket, NDIS_STATUS_SUCCESS);
+#else
+ cAdaptRefs--;
+ cNetFltRefs--;
+#endif
+ }
+ }
+
+ if (cNetFltRefs)
+ {
+ vboxNetFltWinDecReferenceNetFlt(pNetFlt, cNetFltRefs);
+ }
+ else if (cPassThruRefs)
+ {
+ vboxNetFltWinDecReferenceModePassThru(pNetFlt, cPassThruRefs);
+ }
+ if (cAdaptRefs)
+ {
+ vboxNetFltWinDecReferenceWinIf(pNetFlt, cAdaptRefs);
+ }
+ }
+ else
+ {
+ NDIS_HANDLE h = pNetFlt->u.s.WinIf.hMiniport;
+ AssertFailed();
+ if (h)
+ {
+ for (UINT i = 0; i < cNumberOfPackets; i++)
+ {
+ PNDIS_PACKET pPacket;
+ pPacket = pPacketArray[i];
+ NdisMSendComplete(h, pPacket, NDIS_STATUS_FAILURE);
+ }
+ }
+ }
+
+ LogFlowFunc(("LEAVE: pNetFlt (0x%p)\n", pNetFlt));
+}
+
+#ifndef VBOXNETADP
+static UINT vboxNetFltWinMpRequestStatePrep(PVBOXNETFLTINS pNetFlt, NDIS_STATUS *pStatus)
+{
+ Assert(!pNetFlt->u.s.WinIf.StateFlags.fRequestInfo);
+
+ if (vboxNetFltWinGetOpState(&pNetFlt->u.s.WinIf.PtState) > kVBoxNetDevOpState_Initialized /* protocol unbind in progress */
+ || vboxNetFltWinGetPowerState(&pNetFlt->u.s.WinIf.MpState) > NdisDeviceStateD0)
+ {
+ *pStatus = NDIS_STATUS_FAILURE;
+ return 0;
+ }
+
+ RTSpinlockAcquire(pNetFlt->hSpinlock);
+ Assert(!pNetFlt->u.s.WinIf.StateFlags.fRequestInfo);
+ if (vboxNetFltWinGetOpState(&pNetFlt->u.s.WinIf.PtState) > kVBoxNetDevOpState_Initialized /* protocol unbind in progress */
+ || vboxNetFltWinGetPowerState(&pNetFlt->u.s.WinIf.MpState) > NdisDeviceStateD0)
+ {
+ RTSpinlockRelease(pNetFlt->hSpinlock);
+ *pStatus = NDIS_STATUS_FAILURE;
+ return 0;
+ }
+
+ if ((vboxNetFltWinGetPowerState(&pNetFlt->u.s.WinIf.PtState) > NdisDeviceStateD0)
+ && !pNetFlt->u.s.WinIf.StateFlags.fStandBy)
+ {
+ pNetFlt->u.s.WinIf.StateFlags.fRequestInfo = VBOXNDISREQUEST_INPROGRESS | VBOXNDISREQUEST_QUEUED;
+ RTSpinlockRelease(pNetFlt->hSpinlock);
+ *pStatus = NDIS_STATUS_PENDING;
+ return VBOXNDISREQUEST_INPROGRESS | VBOXNDISREQUEST_QUEUED;
+ }
+
+ if (pNetFlt->u.s.WinIf.StateFlags.fStandBy)
+ {
+ RTSpinlockRelease(pNetFlt->hSpinlock);
+ *pStatus = NDIS_STATUS_FAILURE;
+ return 0;
+ }
+
+ pNetFlt->u.s.WinIf.StateFlags.fRequestInfo = VBOXNDISREQUEST_INPROGRESS;
+
+ RTSpinlockRelease(pNetFlt->hSpinlock);
+
+ *pStatus = NDIS_STATUS_SUCCESS;
+ return VBOXNDISREQUEST_INPROGRESS;
+}
+
+static NDIS_STATUS vboxNetFltWinMpRequestPostQuery(PVBOXNETFLTINS pNetFlt)
+{
+ if (pNetFlt->u.s.WinIf.PassDownRequest.DATA.QUERY_INFORMATION.Oid == OID_GEN_CURRENT_PACKET_FILTER && VBOXNETFLT_PROMISCUOUS_SUPPORTED(pNetFlt))
+ {
+ bool fNetFltActive;
+ const bool fWinIfActive = vboxNetFltWinReferenceWinIfNetFlt(pNetFlt, &fNetFltActive);
+
+ Assert(pNetFlt->u.s.WinIf.PassDownRequest.DATA.QUERY_INFORMATION.InformationBuffer);
+ Assert(!pNetFlt->u.s.WinIf.StateFlags.fProcessingPacketFilter);
+
+ if (fNetFltActive)
+ {
+ /* netflt is active, simply return the cached value */
+ *((PULONG)pNetFlt->u.s.WinIf.PassDownRequest.DATA.QUERY_INFORMATION.InformationBuffer) = pNetFlt->u.s.WinIf.fUpperProtocolSetFilter;
+
+ /* we've intercepted the query and completed it */
+ vboxNetFltWinMpRequestStateComplete(pNetFlt);
+
+ vboxNetFltWinDereferenceNetFlt(pNetFlt);
+ vboxNetFltWinDereferenceWinIf(pNetFlt);
+
+ return NDIS_STATUS_SUCCESS;
+ }
+ else if (fWinIfActive)
+ {
+ pNetFlt->u.s.WinIf.StateFlags.fProcessingPacketFilter = 1;
+ pNetFlt->u.s.WinIf.StateFlags.fPPFNetFlt = 0;
+ /* we're cleaning it in RequestComplete */
+ }
+ }
+
+ NDIS_STATUS Status;
+ /* issue the request */
+ NdisRequest(&Status, pNetFlt->u.s.WinIf.hBinding, &pNetFlt->u.s.WinIf.PassDownRequest);
+ if (Status != NDIS_STATUS_PENDING)
+ {
+ vboxNetFltWinPtRequestComplete(pNetFlt, &pNetFlt->u.s.WinIf.PassDownRequest, Status);
+ Status = NDIS_STATUS_PENDING;
+ }
+
+ return Status;
+}
+
+static NDIS_STATUS vboxNetFltWinMpQueryInformation(IN NDIS_HANDLE MiniportAdapterContext,
+ IN NDIS_OID Oid,
+ IN PVOID InformationBuffer,
+ IN ULONG InformationBufferLength,
+ OUT PULONG BytesWritten,
+ OUT PULONG BytesNeeded)
+{
+ PVBOXNETFLTINS pNetFlt = (PVBOXNETFLTINS)MiniportAdapterContext;
+ NDIS_STATUS Status = NDIS_STATUS_FAILURE;
+
+ LogFlowFunc(("ENTER: pNetFlt (0x%p), Oid (%s)\n", pNetFlt, vboxNetFltWinMpDumpOid(Oid)));
+
+ /* fist check if this is the oid we want to pass down */
+ switch (Oid)
+ {
+ case OID_PNP_QUERY_POWER:
+ Status = NDIS_STATUS_SUCCESS;
+ break;
+ case OID_TCP_TASK_OFFLOAD:
+ case OID_GEN_SUPPORTED_GUIDS:
+ Status = NDIS_STATUS_NOT_SUPPORTED;
+ break;
+ default:
+ {
+ /* the oid is to be passed down,
+ * check the device state if we can do it
+ * and update device state accordingly */
+ UINT uOp = vboxNetFltWinMpRequestStatePrep(pNetFlt, &Status);
+ if (uOp)
+ {
+ /* save the request info */
+ pNetFlt->u.s.WinIf.PassDownRequest.RequestType = NdisRequestQueryInformation;
+ pNetFlt->u.s.WinIf.PassDownRequest.DATA.QUERY_INFORMATION.Oid = Oid;
+ pNetFlt->u.s.WinIf.PassDownRequest.DATA.QUERY_INFORMATION.InformationBuffer = InformationBuffer;
+ pNetFlt->u.s.WinIf.PassDownRequest.DATA.QUERY_INFORMATION.InformationBufferLength = InformationBufferLength;
+ pNetFlt->u.s.WinIf.pcPDRBytesNeeded = BytesNeeded;
+ pNetFlt->u.s.WinIf.pcPDRBytesRW = BytesWritten;
+
+ /* the oid can be processed */
+ if (!(uOp & VBOXNDISREQUEST_QUEUED))
+ {
+ Status = vboxNetFltWinMpRequestPostQuery(pNetFlt);
+ }
+ }
+ break;
+ }
+ }
+
+ LogFlowFunc(("LEAVE: pNetFlt (0x%p), Oid (%s), Status (0x%x)\n", pNetFlt, vboxNetFltWinMpDumpOid(Oid), Status));
+
+ return Status;
+}
+
+#endif /* ifndef VBOXNETADP*/
+
+static NDIS_STATUS vboxNetFltWinMpHandlePowerState(PVBOXNETFLTINS pNetFlt, NDIS_DEVICE_POWER_STATE enmState)
+{
+ if (vboxNetFltWinGetPowerState(&pNetFlt->u.s.WinIf.MpState) > NdisDeviceStateD0
+ && enmState != NdisDeviceStateD0)
+ {
+ /* invalid state transformation */
+ AssertFailed();
+ return NDIS_STATUS_FAILURE;
+ }
+
+#ifndef VBOXNETADP
+ if (vboxNetFltWinGetPowerState(&pNetFlt->u.s.WinIf.MpState) == NdisDeviceStateD0
+ && enmState > NdisDeviceStateD0)
+ {
+ pNetFlt->u.s.WinIf.StateFlags.fStandBy = TRUE;
+ }
+
+ if (vboxNetFltWinGetPowerState(&pNetFlt->u.s.WinIf.MpState) > NdisDeviceStateD0
+ && enmState == NdisDeviceStateD0)
+ {
+ pNetFlt->u.s.WinIf.StateFlags.fStandBy = FALSE;
+ }
+#endif
+
+ vboxNetFltWinSetPowerState(&pNetFlt->u.s.WinIf.MpState, enmState);
+
+#ifndef VBOXNETADP
+ if (pNetFlt->u.s.WinIf.StateFlags.fStandBy == FALSE)
+ {
+ if (pNetFlt->u.s.WinIf.MpIndicatedMediaStatus != pNetFlt->u.s.WinIf.MpUnindicatedMediaStatus)
+ {
+ NdisMIndicateStatus(pNetFlt->u.s.WinIf.hMiniport, pNetFlt->u.s.WinIf.MpUnindicatedMediaStatus, NULL, 0);
+ NdisMIndicateStatusComplete(pNetFlt->u.s.WinIf.hMiniport);
+ pNetFlt->u.s.WinIf.MpIndicatedMediaStatus = pNetFlt->u.s.WinIf.MpUnindicatedMediaStatus;
+ }
+ }
+ else
+ {
+ pNetFlt->u.s.WinIf.MpUnindicatedMediaStatus = pNetFlt->u.s.WinIf.MpIndicatedMediaStatus;
+ }
+#endif
+
+ return NDIS_STATUS_SUCCESS;
+}
+
+#ifndef VBOXNETADP
+static NDIS_STATUS vboxNetFltWinMpRequestPostSet(PVBOXNETFLTINS pNetFlt)
+{
+ if (pNetFlt->u.s.WinIf.PassDownRequest.DATA.SET_INFORMATION.Oid == OID_GEN_CURRENT_PACKET_FILTER && VBOXNETFLT_PROMISCUOUS_SUPPORTED(pNetFlt))
+ {
+ /* need to disable cleaning promiscuous here ?? */
+ bool fNetFltActive;
+ const bool fWinIfActive = vboxNetFltWinReferenceWinIfNetFlt(pNetFlt, &fNetFltActive);
+
+ Assert(pNetFlt->u.s.WinIf.PassDownRequest.DATA.SET_INFORMATION.InformationBuffer);
+ Assert(!pNetFlt->u.s.WinIf.StateFlags.fProcessingPacketFilter);
+
+ if (fNetFltActive)
+ {
+ Assert(fWinIfActive);
+
+ /* netflt is active, update the cached value */
+ /** @todo in case we are are not in promiscuous now, we are issuing a request.
+ * what should we do in case of a failure?
+ * i.e. should we update the fUpperProtocolSetFilter in completion routine in this case? etc. */
+ pNetFlt->u.s.WinIf.fUpperProtocolSetFilter = *((PULONG)pNetFlt->u.s.WinIf.PassDownRequest.DATA.SET_INFORMATION.InformationBuffer);
+ pNetFlt->u.s.WinIf.StateFlags.fUpperProtSetFilterInitialized = TRUE;
+
+ if (!(pNetFlt->u.s.WinIf.fOurSetFilter & NDIS_PACKET_TYPE_PROMISCUOUS))
+ {
+ pNetFlt->u.s.WinIf.fSetFilterBuffer = NDIS_PACKET_TYPE_PROMISCUOUS;
+ pNetFlt->u.s.WinIf.PassDownRequest.DATA.SET_INFORMATION.InformationBuffer = &pNetFlt->u.s.WinIf.fSetFilterBuffer;
+ pNetFlt->u.s.WinIf.PassDownRequest.DATA.SET_INFORMATION.InformationBufferLength = sizeof (pNetFlt->u.s.WinIf.fSetFilterBuffer);
+ pNetFlt->u.s.WinIf.StateFlags.fProcessingPacketFilter = 1;
+ pNetFlt->u.s.WinIf.StateFlags.fPPFNetFlt = 1;
+ /* we'll do dereferencing in request complete */
+ }
+ else
+ {
+ vboxNetFltWinDereferenceNetFlt(pNetFlt);
+ vboxNetFltWinDereferenceWinIf(pNetFlt);
+
+ /* we've intercepted the query and completed it */
+ vboxNetFltWinMpRequestStateComplete(pNetFlt);
+ return NDIS_STATUS_SUCCESS;
+ }
+ }
+ else if (fWinIfActive)
+ {
+ pNetFlt->u.s.WinIf.StateFlags.fProcessingPacketFilter = 1;
+ pNetFlt->u.s.WinIf.StateFlags.fPPFNetFlt = 0;
+ /* dereference on completion */
+ }
+ }
+
+ NDIS_STATUS Status;
+
+ NdisRequest(&Status, pNetFlt->u.s.WinIf.hBinding, &pNetFlt->u.s.WinIf.PassDownRequest);
+ if (Status != NDIS_STATUS_PENDING)
+ {
+ vboxNetFltWinPtRequestComplete(pNetFlt, &pNetFlt->u.s.WinIf.PassDownRequest, Status);
+ }
+
+ return Status;
+}
+
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinMpRequestPost(PVBOXNETFLTINS pNetFlt)
+{
+ switch (pNetFlt->u.s.WinIf.PassDownRequest.RequestType)
+ {
+ case NdisRequestQueryInformation:
+ return vboxNetFltWinMpRequestPostQuery(pNetFlt);
+ case NdisRequestSetInformation:
+ return vboxNetFltWinMpRequestPostSet(pNetFlt);
+ default:
+ AssertBreakpoint();
+ return NDIS_STATUS_FAILURE;
+ }
+}
+
+static NDIS_STATUS vboxNetFltWinMpSetInformation(IN NDIS_HANDLE MiniportAdapterContext,
+ IN NDIS_OID Oid,
+ IN PVOID InformationBuffer,
+ IN ULONG InformationBufferLength,
+ OUT PULONG BytesRead,
+ OUT PULONG BytesNeeded)
+{
+ PVBOXNETFLTINS pNetFlt = (PVBOXNETFLTINS)MiniportAdapterContext;
+ NDIS_STATUS Status = NDIS_STATUS_FAILURE;
+
+ LogFlowFunc(("ENTER: pNetFlt (0x%p), Oid (%s)\n", pNetFlt, vboxNetFltWinMpDumpOid(Oid)));
+
+ switch (Oid)
+ {
+ case OID_PNP_SET_POWER:
+ {
+ if (InformationBufferLength >= sizeof (NDIS_DEVICE_POWER_STATE))
+ {
+ NDIS_DEVICE_POWER_STATE *penmState = (NDIS_DEVICE_POWER_STATE*)InformationBuffer;
+ Status = vboxNetFltWinMpHandlePowerState(pNetFlt, *penmState);
+ }
+ else
+ {
+ Status = NDIS_STATUS_INVALID_LENGTH;
+ }
+
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ *BytesRead = sizeof (NDIS_DEVICE_POWER_STATE);
+ *BytesNeeded = 0;
+ }
+ else
+ {
+ *BytesRead = 0;
+ *BytesNeeded = sizeof (NDIS_DEVICE_POWER_STATE);
+ }
+ break;
+ }
+ default:
+ {
+ /* the oid is to be passed down,
+ * check the device state if we can do it
+ * and update device state accordingly */
+ UINT uOp = vboxNetFltWinMpRequestStatePrep(pNetFlt, &Status);
+ if (uOp)
+ {
+ /* save the request info */
+ pNetFlt->u.s.WinIf.PassDownRequest.RequestType = NdisRequestSetInformation;
+ pNetFlt->u.s.WinIf.PassDownRequest.DATA.SET_INFORMATION.Oid = Oid;
+ pNetFlt->u.s.WinIf.PassDownRequest.DATA.SET_INFORMATION.InformationBuffer = InformationBuffer;
+ pNetFlt->u.s.WinIf.PassDownRequest.DATA.SET_INFORMATION.InformationBufferLength = InformationBufferLength;
+ pNetFlt->u.s.WinIf.pcPDRBytesNeeded = BytesNeeded;
+ pNetFlt->u.s.WinIf.pcPDRBytesRW = BytesRead;
+
+ /* the oid can be processed */
+ if (!(uOp & VBOXNDISREQUEST_QUEUED))
+ {
+ Status = vboxNetFltWinMpRequestPostSet(pNetFlt);
+ }
+ }
+ break;
+ }
+ }
+
+ LogFlowFunc(("LEAVE: pNetFlt (0x%p), Oid (%s), Status (0x%x)\n", pNetFlt, vboxNetFltWinMpDumpOid(Oid), Status));
+
+ return Status;
+}
+#else
+static NDIS_OID g_vboxNetFltWinMpSupportedOids[] =
+{
+ OID_GEN_SUPPORTED_LIST,
+ OID_GEN_HARDWARE_STATUS,
+ OID_GEN_MEDIA_SUPPORTED,
+ OID_GEN_MEDIA_IN_USE,
+ OID_GEN_MAXIMUM_LOOKAHEAD,
+ OID_GEN_CURRENT_LOOKAHEAD,
+ OID_GEN_MAXIMUM_FRAME_SIZE,
+ OID_GEN_MAXIMUM_TOTAL_SIZE,
+ OID_GEN_TRANSMIT_BLOCK_SIZE,
+ OID_GEN_RECEIVE_BLOCK_SIZE,
+ OID_GEN_MAC_OPTIONS,
+ OID_GEN_LINK_SPEED,
+ OID_GEN_TRANSMIT_BUFFER_SPACE,
+ OID_GEN_RECEIVE_BUFFER_SPACE,
+ OID_GEN_VENDOR_ID,
+ OID_GEN_VENDOR_DESCRIPTION,
+ OID_GEN_VENDOR_DRIVER_VERSION,
+ OID_GEN_DRIVER_VERSION,
+ OID_GEN_MAXIMUM_SEND_PACKETS,
+ OID_GEN_MEDIA_CONNECT_STATUS,
+ OID_GEN_CURRENT_PACKET_FILTER,
+ OID_PNP_CAPABILITIES,
+ OID_PNP_QUERY_POWER,
+ OID_GEN_XMIT_OK,
+ OID_GEN_RCV_OK,
+ OID_GEN_XMIT_ERROR,
+ OID_GEN_RCV_ERROR,
+ OID_GEN_RCV_NO_BUFFER,
+ OID_GEN_RCV_CRC_ERROR,
+ OID_GEN_TRANSMIT_QUEUE_LENGTH,
+ OID_PNP_SET_POWER,
+ OID_802_3_PERMANENT_ADDRESS,
+ OID_802_3_CURRENT_ADDRESS,
+ OID_802_3_MULTICAST_LIST,
+ OID_802_3_MAC_OPTIONS,
+ OID_802_3_MAXIMUM_LIST_SIZE,
+ OID_802_3_RCV_ERROR_ALIGNMENT,
+ OID_802_3_XMIT_ONE_COLLISION,
+ OID_802_3_XMIT_MORE_COLLISIONS,
+ OID_802_3_XMIT_DEFERRED,
+ OID_802_3_XMIT_MAX_COLLISIONS,
+ OID_802_3_RCV_OVERRUN,
+ OID_802_3_XMIT_UNDERRUN,
+ OID_802_3_XMIT_HEARTBEAT_FAILURE,
+ OID_802_3_XMIT_TIMES_CRS_LOST,
+ OID_802_3_XMIT_LATE_COLLISIONS,
+};
+
+static NDIS_STATUS vboxNetFltWinMpQueryInformation(IN NDIS_HANDLE MiniportAdapterContext,
+ IN NDIS_OID Oid,
+ IN PVOID InformationBuffer,
+ IN ULONG InformationBufferLength,
+ OUT PULONG BytesWritten,
+ OUT PULONG BytesNeeded)
+{
+ /* static data */
+ static const NDIS_HARDWARE_STATUS enmHwStatus = NdisHardwareStatusReady;
+ static const NDIS_MEDIUM enmMedium = NdisMedium802_3;
+ static NDIS_PNP_CAPABILITIES PnPCaps = {0};
+ static BOOLEAN bPnPCapsInited = FALSE;
+
+ PVBOXNETFLTINS pNetFlt = (PVBOXNETFLTINS)MiniportAdapterContext;
+ NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
+ ULONG64 u64Info = 0;
+ ULONG u32Info = 0;
+ USHORT u16Info = 0;
+ /* default is 4bytes */
+ const void* pvInfo = (void*)&u32Info;
+ ULONG cbInfo = sizeof (u32Info);
+
+ LogFlowFunc(("ENTER: pNetFlt (0x%p), Oid (%s)\n", pNetFlt, vboxNetFltWinMpDumpOid(Oid)));
+
+ *BytesWritten = 0;
+ *BytesNeeded = 0;
+
+ switch (Oid)
+ {
+ case OID_GEN_SUPPORTED_LIST:
+ pvInfo = g_vboxNetFltWinMpSupportedOids;
+ cbInfo = sizeof (g_vboxNetFltWinMpSupportedOids);
+ break;
+
+ case OID_GEN_HARDWARE_STATUS:
+ pvInfo = &enmHwStatus;
+ cbInfo = sizeof (NDIS_HARDWARE_STATUS);
+ break;
+
+ case OID_GEN_MEDIA_SUPPORTED:
+ case OID_GEN_MEDIA_IN_USE:
+ pvInfo = &enmMedium;
+ cbInfo = sizeof (NDIS_MEDIUM);
+ break;
+
+ case OID_GEN_MAXIMUM_LOOKAHEAD:
+ case OID_GEN_CURRENT_LOOKAHEAD:
+ u32Info = VBOXNETADP_MAX_LOOKAHEAD_SIZE;
+ break;
+
+ case OID_GEN_MAXIMUM_FRAME_SIZE:
+ u32Info = VBOXNETADP_MAX_PACKET_SIZE - VBOXNETADP_HEADER_SIZE;
+ break;
+
+ case OID_GEN_MAXIMUM_TOTAL_SIZE:
+ case OID_GEN_TRANSMIT_BLOCK_SIZE:
+ case OID_GEN_RECEIVE_BLOCK_SIZE:
+ u32Info = VBOXNETADP_MAX_PACKET_SIZE;
+ break;
+
+ case OID_GEN_MAC_OPTIONS:
+ u32Info = NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA |
+ NDIS_MAC_OPTION_TRANSFERS_NOT_PEND |
+ NDIS_MAC_OPTION_NO_LOOPBACK;
+ break;
+
+ case OID_GEN_LINK_SPEED:
+ u32Info = VBOXNETADP_LINK_SPEED;
+ break;
+
+ case OID_GEN_TRANSMIT_BUFFER_SPACE:
+ case OID_GEN_RECEIVE_BUFFER_SPACE:
+ u32Info = VBOXNETADP_MAX_PACKET_SIZE * VBOXNETFLT_PACKET_INFO_POOL_SIZE;
+ break;
+
+ case OID_GEN_VENDOR_ID:
+ u32Info = VBOXNETADP_VENDOR_ID;
+ break;
+
+ case OID_GEN_VENDOR_DESCRIPTION:
+ pvInfo = VBOXNETADP_VENDOR_DESC;
+ cbInfo = sizeof (VBOXNETADP_VENDOR_DESC);
+ break;
+
+ case OID_GEN_VENDOR_DRIVER_VERSION:
+ u32Info = VBOXNETADP_VENDOR_DRIVER_VERSION;
+ break;
+
+ case OID_GEN_DRIVER_VERSION:
+ u16Info = (USHORT)((VBOXNETFLT_VERSION_MP_NDIS_MAJOR << 8) + VBOXNETFLT_VERSION_MP_NDIS_MINOR);
+ pvInfo = (PVOID)&u16Info;
+ cbInfo = sizeof (USHORT);
+ break;
+
+ case OID_GEN_MAXIMUM_SEND_PACKETS:
+ u32Info = VBOXNETFLT_PACKET_INFO_POOL_SIZE;
+ break;
+
+ case OID_GEN_MEDIA_CONNECT_STATUS:
+#ifdef VBOXNETADP_REPORT_DISCONNECTED
+ {
+ bool bNetFltActive;
+ bool bActive = vboxNetFltWinReferenceWinIfNetFltFromAdapt(pNetFlt, bNetFltActive);
+ if (bActive && bNetFltActive)
+ {
+ u32Info = NdisMediaStateConnected;
+ }
+ else
+ {
+ u32Info = NdisMediaStateDisconnected;
+ }
+
+ if (bActive)
+ {
+ vboxNetFltWinDereferenceWinIf(pNetFlt);
+ }
+ if (bNetFltActive)
+ {
+ vboxNetFltWinDereferenceNetFlt(pNetFlt);
+ }
+ else
+ {
+ vboxNetFltWinDereferenceModePassThru(pNetFlt);
+ }
+ }
+#else
+ u32Info = NdisMediaStateConnected;
+#endif
+ break;
+
+ case OID_GEN_CURRENT_PACKET_FILTER:
+ u32Info = NDIS_PACKET_TYPE_BROADCAST
+ | NDIS_PACKET_TYPE_DIRECTED
+ | NDIS_PACKET_TYPE_ALL_FUNCTIONAL
+ | NDIS_PACKET_TYPE_ALL_LOCAL
+ | NDIS_PACKET_TYPE_GROUP
+ | NDIS_PACKET_TYPE_MULTICAST;
+ break;
+
+ case OID_PNP_CAPABILITIES:
+ if (!bPnPCapsInited)
+ {
+ PnPCaps.WakeUpCapabilities.MinMagicPacketWakeUp = NdisDeviceStateUnspecified;
+ PnPCaps.WakeUpCapabilities.MinPatternWakeUp = NdisDeviceStateUnspecified;
+ bPnPCapsInited = TRUE;
+ }
+ cbInfo = sizeof (NDIS_PNP_CAPABILITIES);
+ pvInfo = &PnPCaps;
+
+ break;
+
+ case OID_PNP_QUERY_POWER:
+ Status = NDIS_STATUS_SUCCESS;
+ break;
+
+ case OID_GEN_XMIT_OK:
+ u64Info = pNetFlt->u.s.WinIf.cTxSuccess;
+ pvInfo = &u64Info;
+ if (InformationBufferLength >= sizeof (ULONG64) || InformationBufferLength == 0)
+ {
+ cbInfo = sizeof (ULONG64);
+ }
+ else
+ {
+ cbInfo = sizeof (ULONG);
+ }
+ *BytesNeeded = sizeof (ULONG64);
+ break;
+
+ case OID_GEN_RCV_OK:
+ u64Info = pNetFlt->u.s.WinIf.cRxSuccess;
+ pvInfo = &u64Info;
+ if (InformationBufferLength >= sizeof (ULONG64) || InformationBufferLength == 0)
+ {
+ cbInfo = sizeof (ULONG64);
+ }
+ else
+ {
+ cbInfo = sizeof (ULONG);
+ }
+ *BytesNeeded = sizeof (ULONG64);
+ break;
+
+ case OID_GEN_XMIT_ERROR:
+ u32Info = pNetFlt->u.s.WinIf.cTxError;
+ break;
+
+ case OID_GEN_RCV_ERROR:
+ u32Info = pNetFlt->u.s.WinIf.cRxError;
+ break;
+
+ case OID_GEN_RCV_NO_BUFFER:
+ case OID_GEN_RCV_CRC_ERROR:
+ u32Info = 0;
+ break;
+
+ case OID_GEN_TRANSMIT_QUEUE_LENGTH:
+ u32Info = VBOXNETFLT_PACKET_INFO_POOL_SIZE;
+ break;
+
+ case OID_802_3_PERMANENT_ADDRESS:
+ pvInfo = &pNetFlt->u.s.MacAddr;
+ cbInfo = VBOXNETADP_ETH_ADDRESS_LENGTH;
+ break;
+
+ case OID_802_3_CURRENT_ADDRESS:
+ pvInfo = &pNetFlt->u.s.MacAddr;
+ cbInfo = VBOXNETADP_ETH_ADDRESS_LENGTH;
+ break;
+
+ case OID_802_3_MAXIMUM_LIST_SIZE:
+ u32Info = VBOXNETADP_MAX_MCAST_LIST;
+ break;
+
+ case OID_802_3_MAC_OPTIONS:
+ case OID_802_3_RCV_ERROR_ALIGNMENT:
+ case OID_802_3_XMIT_ONE_COLLISION:
+ case OID_802_3_XMIT_MORE_COLLISIONS:
+ case OID_802_3_XMIT_DEFERRED:
+ case OID_802_3_XMIT_MAX_COLLISIONS:
+ case OID_802_3_RCV_OVERRUN:
+ case OID_802_3_XMIT_UNDERRUN:
+ case OID_802_3_XMIT_HEARTBEAT_FAILURE:
+ case OID_802_3_XMIT_TIMES_CRS_LOST:
+ case OID_802_3_XMIT_LATE_COLLISIONS:
+ u32Info = 0;
+ break;
+
+ default:
+ Status = NDIS_STATUS_NOT_SUPPORTED;
+ break;
+ }
+
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ if (cbInfo <= InformationBufferLength)
+ {
+ *BytesWritten = cbInfo;
+ if (cbInfo)
+ NdisMoveMemory(InformationBuffer, pvInfo, cbInfo);
+ }
+ else
+ {
+ *BytesNeeded = cbInfo;
+ Status = NDIS_STATUS_INVALID_LENGTH;
+ }
+ }
+
+
+ LogFlowFunc(("LEAVE: pNetFlt (0x%p), Oid (%s), Status (0x%x)\n", pNetFlt, vboxNetFltWinMpDumpOid(Oid), Status));
+
+ return Status;
+}
+
+static NDIS_STATUS vboxNetFltWinMpSetInformation(IN NDIS_HANDLE MiniportAdapterContext,
+ IN NDIS_OID Oid,
+ IN PVOID InformationBuffer,
+ IN ULONG InformationBufferLength,
+ OUT PULONG BytesRead,
+ OUT PULONG BytesNeeded)
+{
+ PVBOXNETFLTINS pNetFlt = (PVBOXNETFLTINS) MiniportAdapterContext;
+ NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
+
+ LogFlowFunc(("ENTER: pNetFlt (0x%p), Oid (%s)\n", pNetFlt, vboxNetFltWinMpDumpOid(Oid)));
+
+ *BytesRead = 0;
+ *BytesNeeded = 0;
+
+ switch (Oid)
+ {
+ case OID_802_3_MULTICAST_LIST:
+ *BytesRead = InformationBufferLength;
+ if (InformationBufferLength % VBOXNETADP_ETH_ADDRESS_LENGTH)
+ {
+ Status = NDIS_STATUS_INVALID_LENGTH;
+ break;
+ }
+
+ if (InformationBufferLength > (VBOXNETADP_MAX_MCAST_LIST * VBOXNETADP_ETH_ADDRESS_LENGTH))
+ {
+ Status = NDIS_STATUS_MULTICAST_FULL;
+ *BytesNeeded = VBOXNETADP_MAX_MCAST_LIST * VBOXNETADP_ETH_ADDRESS_LENGTH;
+ break;
+ }
+ break;
+
+ case OID_GEN_CURRENT_PACKET_FILTER:
+ if (InformationBufferLength != sizeof (ULONG))
+ {
+ *BytesNeeded = sizeof (ULONG);
+ Status = NDIS_STATUS_INVALID_LENGTH;
+ break;
+ }
+
+ *BytesRead = InformationBufferLength;
+
+ break;
+
+ case OID_GEN_CURRENT_LOOKAHEAD:
+ if (InformationBufferLength != sizeof (ULONG)){
+ *BytesNeeded = sizeof(ULONG);
+ Status = NDIS_STATUS_INVALID_LENGTH;
+ break;
+ }
+
+ break;
+
+ case OID_PNP_SET_POWER:
+ if (InformationBufferLength >= sizeof(NDIS_DEVICE_POWER_STATE))
+ {
+ NDIS_DEVICE_POWER_STATE *penmState = (NDIS_DEVICE_POWER_STATE*)InformationBuffer;
+ Status = vboxNetFltWinMpHandlePowerState(pNetFlt, *penmState);
+ }
+ else
+ {
+ Status = NDIS_STATUS_INVALID_LENGTH;
+ }
+
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ *BytesRead = sizeof (NDIS_DEVICE_POWER_STATE);
+ *BytesNeeded = 0;
+ }
+ else
+ {
+ *BytesRead = 0;
+ *BytesNeeded = sizeof (NDIS_DEVICE_POWER_STATE);
+ }
+ break;
+
+ default:
+ Status = NDIS_STATUS_INVALID_OID;
+ break;
+ }
+
+ LogFlowFunc(("LEAVE: pNetFlt (0x%p), Oid (%s), Status (0x%x)\n", pNetFlt, vboxNetFltWinMpDumpOid(Oid), Status));
+
+ return Status;
+}
+
+#endif
+
+#define VBOXNETFLTDUMP_STRCASE(_t) \
+ case _t: return #_t;
+#define VBOXNETFLTDUMP_STRCASE_UNKNOWN() \
+ default: /*AssertFailed();*/ return "Unknown";
+
+static const char* vboxNetFltWinMpDumpOid(ULONG oid)
+{
+ switch (oid)
+ {
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_SUPPORTED_LIST)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_HARDWARE_STATUS)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_MEDIA_SUPPORTED)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_MEDIA_IN_USE)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_MAXIMUM_LOOKAHEAD)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_MAXIMUM_FRAME_SIZE)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_LINK_SPEED)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_TRANSMIT_BUFFER_SPACE)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_RECEIVE_BUFFER_SPACE)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_TRANSMIT_BLOCK_SIZE)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_RECEIVE_BLOCK_SIZE)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_VENDOR_ID)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_VENDOR_DESCRIPTION)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_CURRENT_PACKET_FILTER)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_CURRENT_LOOKAHEAD)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_DRIVER_VERSION)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_MAXIMUM_TOTAL_SIZE)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_PROTOCOL_OPTIONS)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_MAC_OPTIONS)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_MEDIA_CONNECT_STATUS)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_MAXIMUM_SEND_PACKETS)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_VENDOR_DRIVER_VERSION)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_SUPPORTED_GUIDS)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_NETWORK_LAYER_ADDRESSES)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_TRANSPORT_HEADER_OFFSET)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_MACHINE_NAME)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_RNDIS_CONFIG_PARAMETER)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_VLAN_ID)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_MEDIA_CAPABILITIES)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_PHYSICAL_MEDIUM)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_XMIT_OK)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_RCV_OK)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_XMIT_ERROR)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_RCV_ERROR)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_RCV_NO_BUFFER)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_DIRECTED_BYTES_XMIT)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_DIRECTED_FRAMES_XMIT)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_MULTICAST_BYTES_XMIT)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_MULTICAST_FRAMES_XMIT)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_BROADCAST_BYTES_XMIT)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_BROADCAST_FRAMES_XMIT)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_DIRECTED_BYTES_RCV)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_DIRECTED_FRAMES_RCV)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_MULTICAST_BYTES_RCV)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_MULTICAST_FRAMES_RCV)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_BROADCAST_BYTES_RCV)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_BROADCAST_FRAMES_RCV)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_RCV_CRC_ERROR)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_TRANSMIT_QUEUE_LENGTH)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_GET_TIME_CAPS)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_GET_NETCARD_TIME)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_NETCARD_LOAD)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_DEVICE_PROFILE)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_INIT_TIME_MS)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_RESET_COUNTS)
+ VBOXNETFLTDUMP_STRCASE(OID_GEN_MEDIA_SENSE_COUNTS)
+ VBOXNETFLTDUMP_STRCASE(OID_PNP_CAPABILITIES)
+ VBOXNETFLTDUMP_STRCASE(OID_PNP_SET_POWER)
+ VBOXNETFLTDUMP_STRCASE(OID_PNP_QUERY_POWER)
+ VBOXNETFLTDUMP_STRCASE(OID_PNP_ADD_WAKE_UP_PATTERN)
+ VBOXNETFLTDUMP_STRCASE(OID_PNP_REMOVE_WAKE_UP_PATTERN)
+ VBOXNETFLTDUMP_STRCASE(OID_PNP_ENABLE_WAKE_UP)
+ VBOXNETFLTDUMP_STRCASE(OID_802_3_PERMANENT_ADDRESS)
+ VBOXNETFLTDUMP_STRCASE(OID_802_3_CURRENT_ADDRESS)
+ VBOXNETFLTDUMP_STRCASE(OID_802_3_MULTICAST_LIST)
+ VBOXNETFLTDUMP_STRCASE(OID_802_3_MAXIMUM_LIST_SIZE)
+ VBOXNETFLTDUMP_STRCASE(OID_802_3_MAC_OPTIONS)
+ VBOXNETFLTDUMP_STRCASE(OID_802_3_RCV_ERROR_ALIGNMENT)
+ VBOXNETFLTDUMP_STRCASE(OID_802_3_XMIT_ONE_COLLISION)
+ VBOXNETFLTDUMP_STRCASE(OID_802_3_XMIT_MORE_COLLISIONS)
+ VBOXNETFLTDUMP_STRCASE(OID_802_3_XMIT_DEFERRED)
+ VBOXNETFLTDUMP_STRCASE(OID_802_3_XMIT_MAX_COLLISIONS)
+ VBOXNETFLTDUMP_STRCASE(OID_802_3_RCV_OVERRUN)
+ VBOXNETFLTDUMP_STRCASE(OID_802_3_XMIT_UNDERRUN)
+ VBOXNETFLTDUMP_STRCASE(OID_802_3_XMIT_HEARTBEAT_FAILURE)
+ VBOXNETFLTDUMP_STRCASE(OID_802_3_XMIT_TIMES_CRS_LOST)
+ VBOXNETFLTDUMP_STRCASE(OID_802_3_XMIT_LATE_COLLISIONS)
+ VBOXNETFLTDUMP_STRCASE(OID_TCP_TASK_OFFLOAD)
+ VBOXNETFLTDUMP_STRCASE(OID_TCP_TASK_IPSEC_ADD_SA)
+ VBOXNETFLTDUMP_STRCASE(OID_TCP_TASK_IPSEC_DELETE_SA)
+ VBOXNETFLTDUMP_STRCASE(OID_TCP_SAN_SUPPORT)
+ VBOXNETFLTDUMP_STRCASE(OID_TCP_TASK_IPSEC_ADD_UDPESP_SA)
+ VBOXNETFLTDUMP_STRCASE(OID_TCP_TASK_IPSEC_DELETE_UDPESP_SA)
+ VBOXNETFLTDUMP_STRCASE_UNKNOWN()
+ }
+}
+
+DECLHIDDEN(VOID) vboxNetFltWinMpReturnPacket(IN NDIS_HANDLE hMiniportAdapterContext, IN PNDIS_PACKET pPacket)
+{
+ PVBOXNETFLTINS pNetFlt = (PVBOXNETFLTINS)hMiniportAdapterContext;
+ PVBOXNETFLT_PKTRSVD_MP pInfo = (PVBOXNETFLT_PKTRSVD_MP)pPacket->MiniportReserved;
+ PNDIS_PACKET pOrigPacket = pInfo->pOrigPacket;
+ PVOID pBufToFree = pInfo->pBufToFree;
+
+ LogFlowFunc(("ENTER: pNetFlt (0x%p)\n", pNetFlt));
+
+ if (pOrigPacket)
+ {
+ /* the packet was sent from underlying miniport */
+ NdisFreePacket(pPacket);
+ NdisReturnPackets(&pOrigPacket, 1);
+ }
+ else
+ {
+ /* the packet was sent from IntNet or it is a packet we allocated on PtReceive for TransferData processing */
+ vboxNetFltWinFreeSGNdisPacket(pPacket, !pBufToFree /* bFreeMem */);
+ }
+
+ if (pBufToFree)
+ {
+ vboxNetFltWinMemFree(pBufToFree);
+ }
+
+ vboxNetFltWinDereferenceWinIf(pNetFlt);
+
+ LogFlowFunc(("LEAVE: pNetFlt (0x%p)\n", pNetFlt));
+}
+
+static NDIS_STATUS vboxNetFltWinMpTransferData(OUT PNDIS_PACKET Packet,
+ OUT PUINT BytesTransferred,
+ IN NDIS_HANDLE hContext,
+ IN NDIS_HANDLE MiniportReceiveContext,
+ IN UINT ByteOffset,
+ IN UINT BytesToTransfer)
+{
+#ifndef VBOXNETADP
+ PVBOXNETFLTINS pNetFlt = (PVBOXNETFLTINS)hContext;
+ NDIS_STATUS Status;
+
+ LogFlowFunc(("ENTER: pNetFlt (0x%p)\n", pNetFlt));
+
+ if ( vboxNetFltWinGetPowerState(&pNetFlt->u.s.WinIf.PtState) != NdisDeviceStateD0
+ || vboxNetFltWinGetPowerState(&pNetFlt->u.s.WinIf.MpState) != NdisDeviceStateD0)
+ {
+ LogFlowFunc(("LEAVE: pNetFlt (0x%p), Status (0x%x)\n", pNetFlt, NDIS_STATUS_FAILURE));
+ return NDIS_STATUS_FAILURE;
+ }
+
+ NdisTransferData(&Status, pNetFlt->u.s.WinIf.hBinding, MiniportReceiveContext,
+ ByteOffset, BytesToTransfer, Packet, BytesTransferred);
+
+ LogFlowFunc(("LEAVE: pNetFlt (0x%p), Status (0x%x)\n", pNetFlt, Status));
+ return Status;
+
+#else
+ RT_NOREF6(Packet, BytesTransferred, hContext, MiniportReceiveContext, ByteOffset, BytesToTransfer);
+ LogFlowFunc(("ENTER: pNetFlt (0x%p)\n", hContext));
+ /* should never be here */
+ AssertFailed();
+ LogFlowFunc(("LEAVE: pNetFlt (0x%p), Status (0x%x)\n", hContext, NDIS_STATUS_FAILURE));
+ return NDIS_STATUS_FAILURE;
+#endif
+}
+
+static void vboxNetFltWinMpHalt(IN NDIS_HANDLE hContext)
+{
+ PVBOXNETFLTINS pNetFlt = (PVBOXNETFLTINS)hContext;
+ NDIS_STATUS Status;
+
+ LogFlowFunc(("ENTER: pNetFlt (0x%p)\n", pNetFlt));
+
+#ifndef VBOXNETADP
+ if (vboxNetFltWinGetWinIfState(pNetFlt) == kVBoxWinIfState_Disconnecting)
+ {
+ Assert(vboxNetFltWinGetOpState(&pNetFlt->u.s.WinIf.MpState) == kVBoxNetDevOpState_Deinitializing);
+ vboxNetFltWinSetOpState(&pNetFlt->u.s.WinIf.MpState, kVBoxNetDevOpState_Deinitializing);
+
+ vboxNetFltWinPtCloseInterface(pNetFlt, &Status);
+
+ Assert(vboxNetFltWinGetOpState(&pNetFlt->u.s.WinIf.PtState) == kVBoxNetDevOpState_Deinitializing);
+ vboxNetFltWinSetOpState(&pNetFlt->u.s.WinIf.PtState, kVBoxNetDevOpState_Deinitialized);
+ vboxNetFltWinSetOpState(&pNetFlt->u.s.WinIf.MpState, kVBoxNetDevOpState_Deinitialized);
+ }
+ else
+#endif
+ {
+ /* we're NOT called from protocolUnbinAdapter, perform a full disconnect */
+ Assert(vboxNetFltWinGetOpState(&pNetFlt->u.s.WinIf.MpState) == kVBoxNetDevOpState_Initialized);
+#ifndef VBOXNETADP
+ AssertBreakpoint();
+#endif
+ Status = vboxNetFltWinDetachFromInterface(pNetFlt, false);
+ Assert(Status == NDIS_STATUS_SUCCESS);
+ }
+
+ LogFlowFunc(("LEAVE: pNetFlt (0x%p)\n", pNetFlt));
+}
+
+/**
+ * register the miniport edge
+ */
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinMpRegister(PVBOXNETFLTGLOBALS_MP pGlobalsMp, PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegistryPathStr)
+{
+ NDIS_MINIPORT_CHARACTERISTICS MpChars;
+
+ NdisMInitializeWrapper(&pGlobalsMp->hNdisWrapper, pDriverObject, pRegistryPathStr, NULL);
+
+ NdisZeroMemory(&MpChars, sizeof (MpChars));
+
+ MpChars.MajorNdisVersion = VBOXNETFLT_VERSION_MP_NDIS_MAJOR;
+ MpChars.MinorNdisVersion = VBOXNETFLT_VERSION_MP_NDIS_MINOR;
+
+ MpChars.HaltHandler = vboxNetFltWinMpHalt;
+ MpChars.InitializeHandler = vboxNetFltWinMpInitialize;
+ MpChars.QueryInformationHandler = vboxNetFltWinMpQueryInformation;
+ MpChars.SetInformationHandler = vboxNetFltWinMpSetInformation;
+ MpChars.TransferDataHandler = vboxNetFltWinMpTransferData;
+ MpChars.ReturnPacketHandler = vboxNetFltWinMpReturnPacket;
+ MpChars.SendPacketsHandler = vboxNetFltWinMpSendPackets;
+
+#ifndef VBOXNETADP
+ NDIS_STATUS Status = NdisIMRegisterLayeredMiniport(pGlobalsMp->hNdisWrapper, &MpChars, sizeof (MpChars), &pGlobalsMp->hMiniport);
+#else
+ NDIS_STATUS Status = NdisMRegisterMiniport(pGlobalsMp->hNdisWrapper, &MpChars, sizeof (MpChars));
+#endif
+ Assert(Status == NDIS_STATUS_SUCCESS);
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ NdisMRegisterUnloadHandler(pGlobalsMp->hNdisWrapper, vboxNetFltWinUnload);
+ }
+
+ return Status;
+}
+
+/**
+ * deregister the miniport edge
+ */
+DECLHIDDEN(VOID) vboxNetFltWinMpDeregister(PVBOXNETFLTGLOBALS_MP pGlobalsMp)
+{
+#ifndef VBOXNETADP
+ NdisIMDeregisterLayeredMiniport(pGlobalsMp->hMiniport);
+#endif
+ NdisTerminateWrapper(pGlobalsMp->hNdisWrapper, NULL);
+
+ NdisZeroMemory(pGlobalsMp, sizeof (*pGlobalsMp));
+}
+
+#ifndef VBOXNETADP
+
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinMpInitializeDevideInstance(PVBOXNETFLTINS pThis)
+{
+ NDIS_STATUS Status;
+ Assert(vboxNetFltWinGetOpState(&pThis->u.s.WinIf.MpState) == kVBoxNetDevOpState_Deinitialized);
+ vboxNetFltWinSetOpState(&pThis->u.s.WinIf.MpState, kVBoxNetDevOpState_Initializing);
+
+ Status = NdisIMInitializeDeviceInstanceEx(g_VBoxNetFltGlobalsWin.Mp.hMiniport,
+ &pThis->u.s.WinIf.MpDeviceName,
+ pThis);
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ if (pThis->u.s.WinIf.OpenCloseStatus == NDIS_STATUS_SUCCESS)
+ {
+ return NDIS_STATUS_SUCCESS;
+ }
+ AssertBreakpoint();
+ vboxNetFltWinMpDeInitializeDeviceInstance(pThis, &Status);
+ Assert(vboxNetFltWinGetOpState(&pThis->u.s.WinIf.MpState) == kVBoxNetDevOpState_Deinitialized);
+ vboxNetFltWinSetOpState(&pThis->u.s.WinIf.MpState, kVBoxNetDevOpState_Deinitialized);
+ return pThis->u.s.WinIf.OpenCloseStatus;
+ }
+
+ Assert(vboxNetFltWinGetOpState(&pThis->u.s.WinIf.MpState) == kVBoxNetDevOpState_Deinitialized);
+ vboxNetFltWinSetOpState(&pThis->u.s.WinIf.MpState, kVBoxNetDevOpState_Deinitialized);
+
+ return Status;
+}
+
+DECLHIDDEN(bool) vboxNetFltWinMpDeInitializeDeviceInstance(PVBOXNETFLTINS pThis, PNDIS_STATUS pStatus)
+{
+ NDIS_STATUS Status;
+
+ if (vboxNetFltWinGetOpState(&pThis->u.s.WinIf.MpState) == kVBoxNetDevOpState_Initializing)
+ {
+ Status = NdisIMCancelInitializeDeviceInstance(g_VBoxNetFltGlobalsWin.Mp.hMiniport, &pThis->u.s.WinIf.MpDeviceName);
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ /* we've canceled the initialization successfully */
+ Assert(pThis->u.s.WinIf.hMiniport == NULL);
+ Assert(vboxNetFltWinGetOpState(&pThis->u.s.WinIf.MpState) == kVBoxNetDevOpState_Deinitialized);
+ vboxNetFltWinSetOpState(&pThis->u.s.WinIf.MpState, kVBoxNetDevOpState_Deinitialized);
+ }
+ else
+ NdisWaitEvent(&pThis->u.s.WinIf.MpInitCompleteEvent, 0);
+ }
+ else
+ Status = NDIS_STATUS_SUCCESS;
+
+ Assert( vboxNetFltWinGetOpState(&pThis->u.s.WinIf.MpState) == kVBoxNetDevOpState_Initialized
+ || vboxNetFltWinGetOpState(&pThis->u.s.WinIf.MpState) == kVBoxNetDevOpState_Deinitialized);
+ if (vboxNetFltWinGetOpState(&pThis->u.s.WinIf.MpState) == kVBoxNetDevOpState_Initialized)
+ {
+ vboxNetFltWinSetOpState(&pThis->u.s.WinIf.MpState, kVBoxNetDevOpState_Deinitializing);
+
+ Status = NdisIMDeInitializeDeviceInstance(pThis->u.s.WinIf.hMiniport);
+
+ vboxNetFltWinSetOpState(&pThis->u.s.WinIf.MpState, kVBoxNetDevOpState_Deinitialized);
+ if (Status != NDIS_STATUS_SUCCESS)
+ Status = NDIS_STATUS_FAILURE;
+
+ *pStatus = Status;
+ return true;
+ }
+
+ Assert(vboxNetFltWinGetOpState(&pThis->u.s.WinIf.MpState) == kVBoxNetDevOpState_Deinitialized);
+ vboxNetFltWinSetOpState(&pThis->u.s.WinIf.MpState, kVBoxNetDevOpState_Deinitialized);
+
+ *pStatus = Status;
+ return false;
+}
+
+#endif /* !VBOXNETADP */
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltM-win.h b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltM-win.h
new file mode 100644
index 00000000..7a904aa8
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltM-win.h
@@ -0,0 +1,67 @@
+/* $Id: VBoxNetFltM-win.h $ */
+/** @file
+ * VBoxNetFltM-win.h - Bridged Networking Driver, Windows Specific Code - Miniport edge API
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_SRC_VBoxNetFlt_win_drv_VBoxNetFltM_win_h
+#define VBOX_INCLUDED_SRC_VBoxNetFlt_win_drv_VBoxNetFltM_win_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinMpRegister(PVBOXNETFLTGLOBALS_MP pGlobalsMp, PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegistryPathStr);
+DECLHIDDEN(VOID) vboxNetFltWinMpDeregister(PVBOXNETFLTGLOBALS_MP pGlobalsMp);
+DECLHIDDEN(VOID) vboxNetFltWinMpReturnPacket(IN NDIS_HANDLE hMiniportAdapterContext, IN PNDIS_PACKET pPacket);
+
+#ifdef VBOXNETADP
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinMpDoInitialization(PVBOXNETFLTINS pThis, NDIS_HANDLE hMiniportAdapter, NDIS_HANDLE hWrapperConfigurationContext);
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinMpDoDeinitialization(PVBOXNETFLTINS pThis);
+
+#else
+
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinMpInitializeDevideInstance(PVBOXNETFLTINS pThis);
+DECLHIDDEN(bool) vboxNetFltWinMpDeInitializeDeviceInstance(PVBOXNETFLTINS pThis, PNDIS_STATUS pStatus);
+
+DECLINLINE(VOID) vboxNetFltWinMpRequestStateComplete(PVBOXNETFLTINS pNetFlt)
+{
+ RTSpinlockAcquire(pNetFlt->hSpinlock);
+ pNetFlt->u.s.WinIf.StateFlags.fRequestInfo = 0;
+ RTSpinlockRelease(pNetFlt->hSpinlock);
+}
+
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinMpRequestPost(PVBOXNETFLTINS pNetFlt);
+#endif
+
+#endif /* !VBOX_INCLUDED_SRC_VBoxNetFlt_win_drv_VBoxNetFltM_win_h */
+
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltM.inf b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltM.inf
new file mode 100644
index 00000000..fcf30fc8
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltM.inf
@@ -0,0 +1,82 @@
+; $Id: VBoxNetFltM.inf $
+;; @file
+; VBoxNetFltM.inf - VirtualBox Bridged Networking Driver inf file Miniport edge
+;
+
+;
+; Copyright (C) 2011-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+[Version]
+signature = "$Windows NT$"
+;cat CatalogFile = VBoxNetFlt.cat
+Class = Net
+ClassGUID = {4d36e972-e325-11ce-bfc1-08002be10318}
+Provider = %ORACLE%
+;DriverPackageType=NdisImMiniport
+;DriverPackageDisplayName=%VBoxNetFltMP_Desc%
+;edit-DriverVer=08/13/2008,1.1.0.1
+
+[ControlFlags]
+ExcludeFromSelect = sun_VBoxNetFltmp
+
+[DestinationDirs]
+DefaultDestDir=12
+; No files to copy
+
+[Manufacturer]
+%ORACLE% = VBoxNetFltMP@COMMA-NT-ARCH@
+
+[VBoxNetFltMP@DOT-NT-ARCH@]
+%VBoxNetFltMP_Desc% = VBoxNetFltMP.ndi, sun_VBoxNetFltmp
+
+[VBoxNetFltMP.ndi]
+Characteristics = 0x29 ;NCF_NOT_USER_REMOVABLE | NCF_VIRTUAL | NCF_HIDDEN
+CopyFiles =
+
+[VBoxNetFltMP.ndi.Services]
+AddService = VBoxNetFlt,0x2, VBoxNetFltMP.AddService
+
+[VBoxNetFltMP.AddService]
+ServiceType = 1 ;SERVICE_KERNEL_DRIVER
+StartType = 3 ;SERVICE_DEMAND_START
+ErrorControl = 1 ;SERVICE_ERROR_NORMAL
+ServiceBinary = %12%\VBoxNetFlt.sys
+
+[VBoxNetFltMP.AddService.AddReg]
+
+[Strings]
+ORACLE = "Oracle Corporation"
+VBoxNetFltMP_Desc = "VirtualBox Bridged Networking Driver Miniport"
+
+[SourceDisksNames]
+
+[SourceDisksFiles]
+
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltP-win.cpp b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltP-win.cpp
new file mode 100644
index 00000000..3e06f3c2
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltP-win.cpp
@@ -0,0 +1,1595 @@
+/* $Id: VBoxNetFltP-win.cpp $ */
+/** @file
+ * VBoxNetFltP-win.cpp - Bridged Networking Driver, Windows Specific Code.
+ * Protocol edge
+ */
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+#include "VBoxNetFltCmn-win.h"
+
+#ifdef VBOXNETADP
+# error "No protocol edge"
+#endif
+
+#define VBOXNETFLT_PT_STATUS_IS_FILTERED(_s) (\
+ (_s) == NDIS_STATUS_MEDIA_CONNECT \
+ || (_s) == NDIS_STATUS_MEDIA_DISCONNECT \
+ )
+
+/**
+ * performs binding to the given adapter
+ */
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinPtDoBinding(PVBOXNETFLTINS pThis, PNDIS_STRING pOurDeviceName, PNDIS_STRING pBindToDeviceName)
+{
+ Assert(pThis->u.s.WinIf.PtState.PowerState == NdisDeviceStateD3);
+ Assert(pThis->u.s.WinIf.PtState.OpState == kVBoxNetDevOpState_Deinitialized);
+ Assert(KeGetCurrentIrql() == PASSIVE_LEVEL);
+
+ vboxNetFltWinSetOpState(&pThis->u.s.WinIf.PtState, kVBoxNetDevOpState_Initializing);
+
+ NDIS_STATUS Status = vboxNetFltWinCopyString(&pThis->u.s.WinIf.MpDeviceName, pOurDeviceName);
+ Assert (Status == NDIS_STATUS_SUCCESS);
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ vboxNetFltWinSetPowerState(&pThis->u.s.WinIf.PtState, NdisDeviceStateD0);
+ pThis->u.s.WinIf.OpenCloseStatus = NDIS_STATUS_SUCCESS;
+
+ UINT iMedium;
+ NDIS_STATUS TmpStatus;
+ NDIS_MEDIUM aenmNdisMedium[] =
+ {
+ /* Ethernet */
+ NdisMedium802_3,
+ /* Wan */
+ NdisMediumWan
+ };
+
+ NdisResetEvent(&pThis->u.s.WinIf.OpenCloseEvent);
+
+ NdisOpenAdapter(&Status, &TmpStatus, &pThis->u.s.WinIf.hBinding, &iMedium,
+ aenmNdisMedium, RT_ELEMENTS(aenmNdisMedium),
+ g_VBoxNetFltGlobalsWin.Pt.hProtocol,
+ pThis,
+ pBindToDeviceName,
+ 0, /* IN UINT OpenOptions, (reserved, should be NULL) */
+ NULL /* IN PSTRING AddressingInformation OPTIONAL */
+ );
+ Assert(Status == NDIS_STATUS_PENDING || Status == STATUS_SUCCESS);
+ if (Status == NDIS_STATUS_PENDING)
+ {
+ NdisWaitEvent(&pThis->u.s.WinIf.OpenCloseEvent, 0);
+ Status = pThis->u.s.WinIf.OpenCloseStatus;
+ }
+
+ Assert(Status == NDIS_STATUS_SUCCESS);
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ Assert(pThis->u.s.WinIf.hBinding);
+ pThis->u.s.WinIf.enmMedium = aenmNdisMedium[iMedium];
+ vboxNetFltWinSetOpState(&pThis->u.s.WinIf.PtState, kVBoxNetDevOpState_Initialized);
+
+ Status = vboxNetFltWinMpInitializeDevideInstance(pThis);
+ Assert(Status == NDIS_STATUS_SUCCESS);
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ return NDIS_STATUS_SUCCESS;
+ }
+ else
+ {
+ LogRelFunc(("vboxNetFltWinMpInitializeDevideInstance failed, Status 0x%x\n", Status));
+ }
+
+ vboxNetFltWinSetOpState(&pThis->u.s.WinIf.PtState, kVBoxNetDevOpState_Deinitializing);
+ vboxNetFltWinPtCloseInterface(pThis, &TmpStatus);
+ Assert(TmpStatus == NDIS_STATUS_SUCCESS);
+ vboxNetFltWinSetOpState(&pThis->u.s.WinIf.PtState, kVBoxNetDevOpState_Deinitialized);
+ }
+ else
+ {
+ LogRelFunc(("NdisOpenAdapter failed, Status (0x%x)", Status));
+ }
+
+ vboxNetFltWinSetOpState(&pThis->u.s.WinIf.PtState, kVBoxNetDevOpState_Deinitialized);
+ pThis->u.s.WinIf.hBinding = NULL;
+ }
+
+ return Status;
+}
+
+static VOID vboxNetFltWinPtBindAdapter(OUT PNDIS_STATUS pStatus,
+ IN NDIS_HANDLE hBindContext,
+ IN PNDIS_STRING pDeviceNameStr,
+ IN PVOID pvSystemSpecific1,
+ IN PVOID pvSystemSpecific2)
+{
+ LogFlowFuncEnter();
+ RT_NOREF2(hBindContext, pvSystemSpecific2);
+
+ NDIS_STATUS Status;
+ NDIS_HANDLE hConfig = NULL;
+
+ NdisOpenProtocolConfiguration(&Status, &hConfig, (PNDIS_STRING)pvSystemSpecific1);
+ Assert(Status == NDIS_STATUS_SUCCESS);
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ PNDIS_CONFIGURATION_PARAMETER pParam;
+ NDIS_STRING UppedBindStr = NDIS_STRING_CONST("UpperBindings");
+ NdisReadConfiguration(&Status, &pParam, hConfig, &UppedBindStr, NdisParameterString);
+ Assert(Status == NDIS_STATUS_SUCCESS);
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ PVBOXNETFLTINS pNetFlt;
+ Status = vboxNetFltWinPtInitBind(&pNetFlt, &pParam->ParameterData.StringData, pDeviceNameStr);
+ Assert(Status == NDIS_STATUS_SUCCESS);
+ }
+
+ NdisCloseConfiguration(hConfig);
+ }
+
+ *pStatus = Status;
+
+ LogFlowFunc(("LEAVE: Status 0x%x\n", Status));
+}
+
+static VOID vboxNetFltWinPtOpenAdapterComplete(IN NDIS_HANDLE hProtocolBindingContext, IN NDIS_STATUS Status, IN NDIS_STATUS OpenErrorStatus)
+{
+ PVBOXNETFLTINS pNetFlt = (PVBOXNETFLTINS)hProtocolBindingContext;
+ RT_NOREF1(OpenErrorStatus);
+
+ LogFlowFunc(("ENTER: pNetFlt (0x%p), Status (0x%x), OpenErrorStatus(0x%x)\n", pNetFlt, Status, OpenErrorStatus));
+ Assert(pNetFlt->u.s.WinIf.OpenCloseStatus == NDIS_STATUS_SUCCESS);
+ Assert(Status == NDIS_STATUS_SUCCESS);
+ if (pNetFlt->u.s.WinIf.OpenCloseStatus == NDIS_STATUS_SUCCESS)
+ {
+ pNetFlt->u.s.WinIf.OpenCloseStatus = Status;
+ Assert(Status == NDIS_STATUS_SUCCESS);
+ if (Status != NDIS_STATUS_SUCCESS)
+ LogRelFunc(("Open Complete status is 0x%x", Status));
+ }
+ else
+ LogRelFunc(("Adapter maintained status is 0x%x", pNetFlt->u.s.WinIf.OpenCloseStatus));
+ NdisSetEvent(&pNetFlt->u.s.WinIf.OpenCloseEvent);
+ LogFlowFunc(("LEAVE: pNetFlt (0x%p), Status (0x%x), OpenErrorStatus(0x%x)\n", pNetFlt, Status, OpenErrorStatus));
+}
+
+static void vboxNetFltWinPtRequestsWaitComplete(PVBOXNETFLTINS pNetFlt)
+{
+ /* wait for request to complete */
+ while (vboxNetFltWinAtomicUoReadWinState(pNetFlt->u.s.WinIf.StateFlags).fRequestInfo == VBOXNDISREQUEST_INPROGRESS)
+ {
+ vboxNetFltWinSleep(2);
+ }
+
+ /*
+ * If the below miniport is going to low power state, complete the queued request
+ */
+ RTSpinlockAcquire(pNetFlt->hSpinlock);
+ if (pNetFlt->u.s.WinIf.StateFlags.fRequestInfo & VBOXNDISREQUEST_QUEUED)
+ {
+ /* mark the request as InProgress before posting it to RequestComplete */
+ pNetFlt->u.s.WinIf.StateFlags.fRequestInfo = VBOXNDISREQUEST_INPROGRESS;
+ RTSpinlockRelease(pNetFlt->hSpinlock);
+ vboxNetFltWinPtRequestComplete(pNetFlt, &pNetFlt->u.s.WinIf.PassDownRequest, NDIS_STATUS_FAILURE);
+ }
+ else
+ {
+ RTSpinlockRelease(pNetFlt->hSpinlock);
+ }
+}
+
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinPtDoUnbinding(PVBOXNETFLTINS pNetFlt, bool bOnUnbind)
+{
+ NDIS_STATUS Status;
+ uint64_t NanoTS = RTTimeSystemNanoTS();
+ int cPPUsage;
+
+ LogFlowFunc(("ENTER: pNetFlt 0x%p\n", pNetFlt));
+
+ Assert(KeGetCurrentIrql() == PASSIVE_LEVEL);
+
+ Assert(vboxNetFltWinGetOpState(&pNetFlt->u.s.WinIf.PtState) == kVBoxNetDevOpState_Initialized);
+
+ RTSpinlockAcquire(pNetFlt->hSpinlock);
+
+ ASMAtomicUoWriteBool(&pNetFlt->fDisconnectedFromHost, true);
+ ASMAtomicUoWriteBool(&pNetFlt->fRediscoveryPending, false);
+ ASMAtomicUoWriteU64(&pNetFlt->NanoTSLastRediscovery, NanoTS);
+
+ vboxNetFltWinSetOpState(&pNetFlt->u.s.WinIf.PtState, kVBoxNetDevOpState_Deinitializing);
+ if (!bOnUnbind)
+ {
+ vboxNetFltWinSetOpState(&pNetFlt->u.s.WinIf.MpState, kVBoxNetDevOpState_Deinitializing);
+ }
+
+ RTSpinlockRelease(pNetFlt->hSpinlock);
+
+ vboxNetFltWinPtRequestsWaitComplete(pNetFlt);
+
+ vboxNetFltWinWaitDereference(&pNetFlt->u.s.WinIf.MpState);
+ vboxNetFltWinWaitDereference(&pNetFlt->u.s.WinIf.PtState);
+
+ /* check packet pool is empty */
+ cPPUsage = NdisPacketPoolUsage(pNetFlt->u.s.WinIf.hSendPacketPool);
+ Assert(cPPUsage == 0);
+ cPPUsage = NdisPacketPoolUsage(pNetFlt->u.s.WinIf.hRecvPacketPool);
+ Assert(cPPUsage == 0);
+ /* for debugging only, ignore the err in release */
+ NOREF(cPPUsage);
+
+ if (!bOnUnbind || !vboxNetFltWinMpDeInitializeDeviceInstance(pNetFlt, &Status))
+ {
+ vboxNetFltWinPtCloseInterface(pNetFlt, &Status);
+ vboxNetFltWinSetOpState(&pNetFlt->u.s.WinIf.PtState, kVBoxNetDevOpState_Deinitialized);
+
+ if (!bOnUnbind)
+ {
+ Assert(vboxNetFltWinGetOpState(&pNetFlt->u.s.WinIf.MpState) == kVBoxNetDevOpState_Deinitializing);
+ vboxNetFltWinSetOpState(&pNetFlt->u.s.WinIf.MpState, kVBoxNetDevOpState_Deinitialized);
+ }
+ else
+ {
+ Assert(vboxNetFltWinGetOpState(&pNetFlt->u.s.WinIf.MpState) == kVBoxNetDevOpState_Deinitialized);
+ }
+ }
+ else
+ {
+ Assert(vboxNetFltWinGetOpState(&pNetFlt->u.s.WinIf.MpState) == kVBoxNetDevOpState_Deinitialized);
+ }
+
+ LogFlowFunc(("LEAVE: pNetFlt 0x%p\n", pNetFlt));
+
+ return Status;
+}
+
+static VOID vboxNetFltWinPtUnbindAdapter(OUT PNDIS_STATUS pStatus,
+ IN NDIS_HANDLE hContext,
+ IN NDIS_HANDLE hUnbindContext)
+{
+ PVBOXNETFLTINS pNetFlt = (PVBOXNETFLTINS)hContext;
+ RT_NOREF1(hUnbindContext);
+
+ LogFlowFunc(("ENTER: pNetFlt (0x%p)\n", pNetFlt));
+
+ *pStatus = vboxNetFltWinDetachFromInterface(pNetFlt, true);
+ Assert(*pStatus == NDIS_STATUS_SUCCESS);
+
+ LogFlowFunc(("LEAVE: pNetFlt (0x%p)\n", pNetFlt));
+}
+
+static VOID vboxNetFltWinPtUnloadProtocol()
+{
+ LogFlowFuncEnter();
+ NDIS_STATUS Status = vboxNetFltWinPtDeregister(&g_VBoxNetFltGlobalsWin.Pt);
+ Assert(Status == NDIS_STATUS_SUCCESS); NOREF(Status);
+ LogFlowFunc(("LEAVE: PtDeregister Status (0x%x)\n", Status));
+}
+
+
+static VOID vboxNetFltWinPtCloseAdapterComplete(IN NDIS_HANDLE ProtocolBindingContext, IN NDIS_STATUS Status)
+{
+ PVBOXNETFLTINS pNetFlt = (PVBOXNETFLTINS)ProtocolBindingContext;
+
+ LogFlowFunc(("ENTER: pNetFlt (0x%p), Status (0x%x)\n", pNetFlt, Status));
+ Assert(pNetFlt->u.s.WinIf.OpenCloseStatus == NDIS_STATUS_SUCCESS);
+ Assert(Status == NDIS_STATUS_SUCCESS);
+ Assert(pNetFlt->u.s.WinIf.OpenCloseStatus == NDIS_STATUS_SUCCESS);
+ if (pNetFlt->u.s.WinIf.OpenCloseStatus == NDIS_STATUS_SUCCESS)
+ {
+ pNetFlt->u.s.WinIf.OpenCloseStatus = Status;
+ }
+ NdisSetEvent(&pNetFlt->u.s.WinIf.OpenCloseEvent);
+ LogFlowFunc(("LEAVE: pNetFlt (0x%p), Status (0x%x)\n", pNetFlt, Status));
+}
+
+static VOID vboxNetFltWinPtResetComplete(IN NDIS_HANDLE hProtocolBindingContext, IN NDIS_STATUS Status)
+{
+ RT_NOREF2(hProtocolBindingContext, Status);
+ LogFlowFunc(("ENTER: pNetFlt 0x%p, Status 0x%x\n", hProtocolBindingContext, Status));
+ /*
+ * should never be here
+ */
+ AssertFailed();
+ LogFlowFunc(("LEAVE: pNetFlt 0x%p, Status 0x%x\n", hProtocolBindingContext, Status));
+}
+
+static NDIS_STATUS vboxNetFltWinPtHandleQueryInfoComplete(PVBOXNETFLTINS pNetFlt, NDIS_STATUS Status)
+{
+ PNDIS_REQUEST pRequest = &pNetFlt->u.s.WinIf.PassDownRequest;
+
+ switch (pRequest->DATA.QUERY_INFORMATION.Oid)
+ {
+ case OID_PNP_CAPABILITIES:
+ {
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ if (pRequest->DATA.QUERY_INFORMATION.InformationBufferLength >= sizeof (NDIS_PNP_CAPABILITIES))
+ {
+ PNDIS_PNP_CAPABILITIES pPnPCaps = (PNDIS_PNP_CAPABILITIES)(pRequest->DATA.QUERY_INFORMATION.InformationBuffer);
+ PNDIS_PM_WAKE_UP_CAPABILITIES pPmWuCaps = &pPnPCaps->WakeUpCapabilities;
+ pPmWuCaps->MinMagicPacketWakeUp = NdisDeviceStateUnspecified;
+ pPmWuCaps->MinPatternWakeUp = NdisDeviceStateUnspecified;
+ pPmWuCaps->MinLinkChangeWakeUp = NdisDeviceStateUnspecified;
+ *pNetFlt->u.s.WinIf.pcPDRBytesRW = sizeof (NDIS_PNP_CAPABILITIES);
+ *pNetFlt->u.s.WinIf.pcPDRBytesNeeded = 0;
+ Status = NDIS_STATUS_SUCCESS;
+ }
+ else
+ {
+ AssertFailed();
+ *pNetFlt->u.s.WinIf.pcPDRBytesNeeded = sizeof(NDIS_PNP_CAPABILITIES);
+ Status = NDIS_STATUS_RESOURCES;
+ }
+ }
+ break;
+ }
+
+ case OID_GEN_MAC_OPTIONS:
+ {
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ if (pRequest->DATA.QUERY_INFORMATION.InformationBufferLength >= sizeof (ULONG))
+ {
+ pNetFlt->u.s.WinIf.fMacOptions = *(PULONG)pRequest->DATA.QUERY_INFORMATION.InformationBuffer;
+#ifndef VBOX_LOOPBACK_USEFLAGS
+ /* clearing this flag tells ndis we'll handle loopback ourselves
+ * the ndis layer or nic driver below us would loopback packets as necessary */
+ *(PULONG)pRequest->DATA.QUERY_INFORMATION.InformationBuffer &= ~NDIS_MAC_OPTION_NO_LOOPBACK;
+#else
+ /* we have to catch loopbacks from the underlying driver, so no duplications will occur,
+ * just indicate NDIS to handle loopbacks for the packets coming from the protocol */
+ *(PULONG)pRequest->DATA.QUERY_INFORMATION.InformationBuffer |= NDIS_MAC_OPTION_NO_LOOPBACK;
+#endif
+ }
+ else
+ {
+ AssertFailed();
+ *pNetFlt->u.s.WinIf.pcPDRBytesNeeded = sizeof (ULONG);
+ Status = NDIS_STATUS_RESOURCES;
+ }
+ }
+ break;
+ }
+
+ case OID_GEN_CURRENT_PACKET_FILTER:
+ {
+ if (VBOXNETFLT_PROMISCUOUS_SUPPORTED(pNetFlt))
+ {
+ /* we're here _ONLY_ in the passthru mode */
+ Assert(pNetFlt->u.s.WinIf.StateFlags.fProcessingPacketFilter && !pNetFlt->u.s.WinIf.StateFlags.fPPFNetFlt);
+ if (pNetFlt->u.s.WinIf.StateFlags.fProcessingPacketFilter && !pNetFlt->u.s.WinIf.StateFlags.fPPFNetFlt)
+ {
+ Assert(pNetFlt->enmTrunkState != INTNETTRUNKIFSTATE_ACTIVE);
+ vboxNetFltWinDereferenceModePassThru(pNetFlt);
+ vboxNetFltWinDereferenceWinIf(pNetFlt);
+ }
+
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ if (pRequest->DATA.QUERY_INFORMATION.InformationBufferLength >= sizeof (ULONG))
+ {
+ /* the filter request is issued below only in case netflt is not active,
+ * simply update the cache here */
+ /* cache the filter used by upper protocols */
+ pNetFlt->u.s.WinIf.fUpperProtocolSetFilter = *(PULONG)pRequest->DATA.QUERY_INFORMATION.InformationBuffer;
+ pNetFlt->u.s.WinIf.StateFlags.fUpperProtSetFilterInitialized = TRUE;
+ }
+ else
+ {
+ AssertFailed();
+ *pNetFlt->u.s.WinIf.pcPDRBytesNeeded = sizeof (ULONG);
+ Status = NDIS_STATUS_RESOURCES;
+ }
+ }
+ }
+ break;
+ }
+
+ default:
+ Assert(pRequest->DATA.QUERY_INFORMATION.Oid != OID_PNP_QUERY_POWER);
+ break;
+ }
+
+ *pNetFlt->u.s.WinIf.pcPDRBytesRW = pRequest->DATA.QUERY_INFORMATION.BytesWritten;
+ *pNetFlt->u.s.WinIf.pcPDRBytesNeeded = pRequest->DATA.QUERY_INFORMATION.BytesNeeded;
+
+ return Status;
+}
+
+static NDIS_STATUS vboxNetFltWinPtHandleSetInfoComplete(PVBOXNETFLTINS pNetFlt, NDIS_STATUS Status)
+{
+ PNDIS_REQUEST pRequest = &pNetFlt->u.s.WinIf.PassDownRequest;
+
+ switch (pRequest->DATA.SET_INFORMATION.Oid)
+ {
+ case OID_GEN_CURRENT_PACKET_FILTER:
+ {
+ if (VBOXNETFLT_PROMISCUOUS_SUPPORTED(pNetFlt))
+ {
+ Assert(Status == NDIS_STATUS_SUCCESS);
+ if (pNetFlt->u.s.WinIf.StateFlags.fProcessingPacketFilter)
+ {
+ if (pNetFlt->u.s.WinIf.StateFlags.fPPFNetFlt)
+ {
+ Assert(pNetFlt->enmTrunkState == INTNETTRUNKIFSTATE_ACTIVE);
+ pNetFlt->u.s.WinIf.StateFlags.fPPFNetFlt = 0;
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ if (pRequest->DATA.SET_INFORMATION.InformationBufferLength >= sizeof (ULONG))
+ {
+ pNetFlt->u.s.WinIf.fOurSetFilter = *((PULONG)pRequest->DATA.SET_INFORMATION.InformationBuffer);
+ Assert(pNetFlt->u.s.WinIf.fOurSetFilter == NDIS_PACKET_TYPE_PROMISCUOUS);
+ }
+ else
+ {
+ AssertFailed();
+ *pNetFlt->u.s.WinIf.pcPDRBytesNeeded = sizeof (ULONG);
+ Status = NDIS_STATUS_RESOURCES;
+ }
+ }
+ vboxNetFltWinDereferenceNetFlt(pNetFlt);
+ }
+ else
+ {
+ Assert(pNetFlt->enmTrunkState != INTNETTRUNKIFSTATE_ACTIVE);
+
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ if (pRequest->DATA.SET_INFORMATION.InformationBufferLength >= sizeof (ULONG))
+ {
+ /* the request was issued when the netflt was not active, simply update the cache here */
+ pNetFlt->u.s.WinIf.fUpperProtocolSetFilter = *((PULONG)pRequest->DATA.SET_INFORMATION.InformationBuffer);
+ pNetFlt->u.s.WinIf.StateFlags.fUpperProtSetFilterInitialized = TRUE;
+ }
+ else
+ {
+ AssertFailed();
+ *pNetFlt->u.s.WinIf.pcPDRBytesNeeded = sizeof (ULONG);
+ Status = NDIS_STATUS_RESOURCES;
+ }
+ }
+ vboxNetFltWinDereferenceModePassThru(pNetFlt);
+ }
+
+ pNetFlt->u.s.WinIf.StateFlags.fProcessingPacketFilter = 0;
+ vboxNetFltWinDereferenceWinIf(pNetFlt);
+ }
+#ifdef DEBUG_misha
+ else
+ {
+ AssertFailed();
+ }
+#endif
+ }
+ break;
+ }
+
+ default:
+ Assert(pRequest->DATA.SET_INFORMATION.Oid != OID_PNP_SET_POWER);
+ break;
+ }
+
+ *pNetFlt->u.s.WinIf.pcPDRBytesRW = pRequest->DATA.SET_INFORMATION.BytesRead;
+ *pNetFlt->u.s.WinIf.pcPDRBytesNeeded = pRequest->DATA.SET_INFORMATION.BytesNeeded;
+
+ return Status;
+}
+
+DECLHIDDEN(VOID) vboxNetFltWinPtRequestComplete(NDIS_HANDLE hContext, PNDIS_REQUEST pNdisRequest, NDIS_STATUS Status)
+{
+ PVBOXNETFLTINS pNetFlt = (PVBOXNETFLTINS)hContext;
+ PNDIS_REQUEST pSynchRequest = pNetFlt->u.s.WinIf.pSynchRequest;
+
+ LogFlowFunc(("ENTER: pNetFlt (0x%p), pNdisRequest (0x%p), Status (0x%x)\n", pNetFlt, pNdisRequest, Status));
+
+ if (pSynchRequest == pNdisRequest)
+ {
+ /* asynchronous completion of our sync request */
+ /*1.set the status */
+ pNetFlt->u.s.WinIf.SynchCompletionStatus = Status;
+ /* 2. set event */
+ KeSetEvent(&pNetFlt->u.s.WinIf.hSynchCompletionEvent, 0, FALSE);
+ /* 3. return; */
+
+ LogFlowFunc(("LEAVE: pNetFlt (0x%p), pNdisRequest (0x%p), Status (0x%x)\n", pNetFlt, pNdisRequest, Status));
+ return;
+ }
+
+ Assert(&pNetFlt->u.s.WinIf.PassDownRequest == pNdisRequest);
+ Assert(pNetFlt->u.s.WinIf.StateFlags.fRequestInfo == VBOXNDISREQUEST_INPROGRESS);
+ vboxNetFltWinMpRequestStateComplete(pNetFlt);
+
+ switch (pNdisRequest->RequestType)
+ {
+ case NdisRequestQueryInformation:
+ Status = vboxNetFltWinPtHandleQueryInfoComplete(pNetFlt, Status);
+ NdisMQueryInformationComplete(pNetFlt->u.s.WinIf.hMiniport, Status);
+ break;
+
+ case NdisRequestSetInformation:
+ Status = vboxNetFltWinPtHandleSetInfoComplete(pNetFlt, Status);
+ NdisMSetInformationComplete(pNetFlt->u.s.WinIf.hMiniport, Status);
+ break;
+
+ default:
+ AssertFailed();
+ break;
+ }
+
+ LogFlowFunc(("LEAVE: pNetFlt (0x%p), pNdisRequest (0x%p), Status (0x%x)\n", pNetFlt, pNdisRequest, Status));
+}
+
+static VOID vboxNetFltWinPtStatus(IN NDIS_HANDLE hProtocolBindingContext, IN NDIS_STATUS GeneralStatus, IN PVOID pvStatusBuffer, IN UINT cbStatusBuffer)
+{
+ PVBOXNETFLTINS pNetFlt = (PVBOXNETFLTINS)hProtocolBindingContext;
+
+ LogFlowFunc(("ENTER: pNetFlt (0x%p), GeneralStatus (0x%x)\n", pNetFlt, GeneralStatus));
+
+ if (vboxNetFltWinReferenceWinIf(pNetFlt))
+ {
+ Assert(pNetFlt->u.s.WinIf.hMiniport);
+
+ if (VBOXNETFLT_PT_STATUS_IS_FILTERED(GeneralStatus))
+ {
+ pNetFlt->u.s.WinIf.MpIndicatedMediaStatus = GeneralStatus;
+ }
+ NdisMIndicateStatus(pNetFlt->u.s.WinIf.hMiniport,
+ GeneralStatus,
+ pvStatusBuffer,
+ cbStatusBuffer);
+
+ vboxNetFltWinDereferenceWinIf(pNetFlt);
+ }
+ else
+ {
+ if (pNetFlt->u.s.WinIf.hMiniport != NULL
+ && VBOXNETFLT_PT_STATUS_IS_FILTERED(GeneralStatus)
+ )
+ {
+ pNetFlt->u.s.WinIf.MpUnindicatedMediaStatus = GeneralStatus;
+ }
+ }
+
+ LogFlowFunc(("LEAVE: pNetFlt (0x%p), GeneralStatus (0x%x)\n", pNetFlt, GeneralStatus));
+}
+
+
+static VOID vboxNetFltWinPtStatusComplete(IN NDIS_HANDLE hProtocolBindingContext)
+{
+ PVBOXNETFLTINS pNetFlt = (PVBOXNETFLTINS)hProtocolBindingContext;
+
+ LogFlowFunc(("ENTER: pNetFlt (0x%p)\n", pNetFlt));
+
+ if (vboxNetFltWinReferenceWinIf(pNetFlt))
+ {
+ NdisMIndicateStatusComplete(pNetFlt->u.s.WinIf.hMiniport);
+
+ vboxNetFltWinDereferenceWinIf(pNetFlt);
+ }
+
+ LogFlowFunc(("LEAVE: pNetFlt (0x%p)\n", pNetFlt));
+}
+
+static VOID vboxNetFltWinPtSendComplete(IN NDIS_HANDLE hProtocolBindingContext, IN PNDIS_PACKET pPacket, IN NDIS_STATUS Status)
+{
+ PVBOXNETFLTINS pNetFlt = (PVBOXNETFLTINS)hProtocolBindingContext;
+ PVBOXNETFLT_PKTRSVD_PT pSendInfo = (PVBOXNETFLT_PKTRSVD_PT)pPacket->ProtocolReserved;
+ PNDIS_PACKET pOrigPacket = pSendInfo->pOrigPacket;
+ PVOID pBufToFree = pSendInfo->pBufToFree;
+ LogFlowFunc(("ENTER: pNetFlt (0x%p), pPacket (0x%p), Status (0x%x)\n", pNetFlt, pPacket, Status));
+
+#if defined(DEBUG_NETFLT_PACKETS) || !defined(VBOX_LOOPBACK_USEFLAGS)
+ /** @todo for optimization we could check only for netflt-mode packets
+ * do it for all for now */
+ vboxNetFltWinLbRemoveSendPacket(pNetFlt, pPacket);
+#endif
+
+ if (pOrigPacket)
+ {
+ NdisIMCopySendCompletePerPacketInfo(pOrigPacket, pPacket);
+ NdisFreePacket(pPacket);
+ /* the ptk was posted from the upperlying protocol */
+ NdisMSendComplete(pNetFlt->u.s.WinIf.hMiniport, pOrigPacket, Status);
+ }
+ else
+ {
+ /* if the pOrigPacket is zero - the ptk was originated by netFlt send/receive
+ * need to free packet buffers */
+ vboxNetFltWinFreeSGNdisPacket(pPacket, !pBufToFree);
+ }
+
+ if (pBufToFree)
+ {
+ vboxNetFltWinMemFree(pBufToFree);
+ }
+
+ vboxNetFltWinDereferenceWinIf(pNetFlt);
+
+ LogFlowFunc(("LEAVE: pNetFlt (0x%p), pPacket (0x%p), Status (0x%x)\n", pNetFlt, pPacket, Status));
+}
+
+/**
+ * removes searches for the packet in the list and removes it if found
+ * @return true if the packet was found and removed, false - otherwise
+ */
+static bool vboxNetFltWinRemovePacketFromList(PVBOXNETFLT_INTERLOCKED_SINGLE_LIST pList, PNDIS_PACKET pPacket)
+{
+ PVBOXNETFLT_PKTRSVD_TRANSFERDATA_PT pTDR = (PVBOXNETFLT_PKTRSVD_TRANSFERDATA_PT)pPacket->ProtocolReserved;
+ return vboxNetFltWinInterlockedSearchListEntry(pList, &pTDR->ListEntry, true /* remove*/);
+}
+
+/**
+ * puts the packet to the tail of the list
+ */
+static void vboxNetFltWinPutPacketToList(PVBOXNETFLT_INTERLOCKED_SINGLE_LIST pList, PNDIS_PACKET pPacket, PNDIS_BUFFER pOrigBuffer)
+{
+ PVBOXNETFLT_PKTRSVD_TRANSFERDATA_PT pTDR = (PVBOXNETFLT_PKTRSVD_TRANSFERDATA_PT)pPacket->ProtocolReserved;
+ pTDR->pOrigBuffer = pOrigBuffer;
+ vboxNetFltWinInterlockedPutTail(pList, &pTDR->ListEntry);
+}
+
+static bool vboxNetFltWinPtTransferDataCompleteActive(PVBOXNETFLTINS pNetFltIf, PNDIS_PACKET pPacket, NDIS_STATUS Status)
+{
+ PNDIS_BUFFER pBuffer;
+ PVBOXNETFLT_PKTRSVD_TRANSFERDATA_PT pTDR;
+
+ if (!vboxNetFltWinRemovePacketFromList(&pNetFltIf->u.s.WinIf.TransferDataList, pPacket))
+ return false;
+
+ pTDR = (PVBOXNETFLT_PKTRSVD_TRANSFERDATA_PT)pPacket->ProtocolReserved;
+ Assert(pTDR);
+ Assert(pTDR->pOrigBuffer);
+
+ do
+ {
+ NdisUnchainBufferAtFront(pPacket, &pBuffer);
+
+ Assert(pBuffer);
+
+ NdisFreeBuffer(pBuffer);
+
+ pBuffer = pTDR->pOrigBuffer;
+
+ NdisChainBufferAtBack(pPacket, pBuffer);
+
+ /* data transfer was initiated when the netFlt was active
+ * the netFlt is still retained by us
+ * 1. check if loopback
+ * 2. enqueue packet
+ * 3. release netFlt */
+
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+
+#ifdef VBOX_LOOPBACK_USEFLAGS
+ if (vboxNetFltWinIsLoopedBackPacket(pPacket))
+ {
+ /* should not be here */
+ AssertFailed();
+ }
+#else
+ PNDIS_PACKET pLb = vboxNetFltWinLbSearchLoopBack(pNetFltIf, pPacket, false);
+ if (pLb)
+ {
+#ifndef DEBUG_NETFLT_RECV_TRANSFERDATA
+ /* should not be here */
+ AssertFailed();
+#endif
+ if (!vboxNetFltWinLbIsFromIntNet(pLb))
+ {
+ /* the packet is not from int net, need to pass it up to the host */
+ NdisMIndicateReceivePacket(pNetFltIf->u.s.WinIf.hMiniport, &pPacket, 1);
+ /* dereference NetFlt, WinIf will be dereferenced on Packet return */
+ vboxNetFltWinDereferenceNetFlt(pNetFltIf);
+ break;
+ }
+ }
+#endif
+ else
+ {
+ /* 2. enqueue */
+ /* use the same packet info to put the packet in the processing packet queue */
+ PVBOXNETFLT_PKTRSVD_MP pRecvInfo = (PVBOXNETFLT_PKTRSVD_MP)pPacket->MiniportReserved;
+
+ VBOXNETFLT_LBVERIFY(pNetFltIf, pPacket);
+
+ pRecvInfo->pOrigPacket = NULL;
+ pRecvInfo->pBufToFree = NULL;
+
+ NdisGetPacketFlags(pPacket) = 0;
+# ifdef VBOXNETFLT_NO_PACKET_QUEUE
+ if (vboxNetFltWinPostIntnet(pNetFltIf, pPacket, 0))
+ {
+ /* drop it */
+ vboxNetFltWinFreeSGNdisPacket(pPacket, true);
+ vboxNetFltWinDereferenceWinIf(pNetFltIf);
+ }
+ else
+ {
+ NdisMIndicateReceivePacket(pNetFltIf->u.s.WinIf.hMiniport, &pPacket, 1);
+ }
+ vboxNetFltWinDereferenceNetFlt(pNetFltIf);
+ break;
+# else
+ Status = vboxNetFltWinQuEnqueuePacket(pNetFltIf, pPacket, PACKET_MINE);
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ break;
+ }
+ AssertFailed();
+# endif
+ }
+ }
+ else
+ {
+ AssertFailed();
+ }
+ /* we are here because of error either in data transfer or in enqueueing the packet */
+ vboxNetFltWinFreeSGNdisPacket(pPacket, true);
+ vboxNetFltWinDereferenceNetFlt(pNetFltIf);
+ vboxNetFltWinDereferenceWinIf(pNetFltIf);
+ } while (0);
+
+ return true;
+}
+
+static VOID vboxNetFltWinPtTransferDataComplete(IN NDIS_HANDLE hProtocolBindingContext,
+ IN PNDIS_PACKET pPacket,
+ IN NDIS_STATUS Status,
+ IN UINT cbTransferred)
+{
+ PVBOXNETFLTINS pNetFlt = (PVBOXNETFLTINS)hProtocolBindingContext;
+ LogFlowFunc(("ENTER: pNetFlt (0x%p), pPacket (0x%p), Status (0x%x), cbTransfered (%d)\n", pNetFlt, pPacket, Status, cbTransferred));
+ if (!vboxNetFltWinPtTransferDataCompleteActive(pNetFlt, pPacket, Status))
+ {
+ if (pNetFlt->u.s.WinIf.hMiniport)
+ {
+ NdisMTransferDataComplete(pNetFlt->u.s.WinIf.hMiniport,
+ pPacket,
+ Status,
+ cbTransferred);
+ }
+
+ vboxNetFltWinDereferenceWinIf(pNetFlt);
+ }
+ /* else - all processing is done with vboxNetFltWinPtTransferDataCompleteActive already */
+
+ LogFlowFunc(("LEAVE: pNetFlt (0x%p), pPacket (0x%p), Status (0x%x), cbTransfered (%d)\n", pNetFlt, pPacket, Status, cbTransferred));
+}
+
+static INT vboxNetFltWinRecvPacketPassThru(PVBOXNETFLTINS pNetFlt, PNDIS_PACKET pPacket)
+{
+ Assert(KeGetCurrentIrql() == DISPATCH_LEVEL);
+
+ PNDIS_PACKET pMyPacket;
+ NDIS_STATUS Status = vboxNetFltWinPrepareRecvPacket(pNetFlt, pPacket, &pMyPacket, true);
+ /* the Status holds the current packet status it will be checked for NDIS_STATUS_RESOURCES later
+ * (see below) */
+ Assert(pMyPacket);
+ if (pMyPacket)
+ {
+ NdisMIndicateReceivePacket(pNetFlt->u.s.WinIf.hMiniport, &pMyPacket, 1);
+ if (Status == NDIS_STATUS_RESOURCES)
+ {
+ NdisDprFreePacket(pMyPacket);
+ return 0;
+ }
+
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * process the packet receive in a "passthru" mode
+ */
+static NDIS_STATUS vboxNetFltWinRecvPassThru(PVBOXNETFLTINS pNetFlt, PNDIS_PACKET pPacket)
+{
+ Assert(KeGetCurrentIrql() == DISPATCH_LEVEL);
+
+ NDIS_STATUS Status;
+ PNDIS_PACKET pMyPacket;
+
+ NdisDprAllocatePacket(&Status, &pMyPacket, pNetFlt->u.s.WinIf.hRecvPacketPool);
+ Assert(Status == NDIS_STATUS_SUCCESS);
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ vboxNetFltWinCopyPacketInfoOnRecv(pMyPacket, pPacket, true /* force NDIS_STATUS_RESOURCES */);
+ Assert(NDIS_GET_PACKET_STATUS(pMyPacket) == NDIS_STATUS_RESOURCES);
+
+ NdisMIndicateReceivePacket(pNetFlt->u.s.WinIf.hMiniport, &pMyPacket, 1);
+
+ NdisDprFreePacket(pMyPacket);
+ }
+ return Status;
+}
+
+static VOID vboxNetFltWinRecvIndicatePassThru(PVBOXNETFLTINS pNetFlt, NDIS_HANDLE MacReceiveContext,
+ PVOID pHeaderBuffer, UINT cbHeaderBuffer, PVOID pLookAheadBuffer, UINT cbLookAheadBuffer, UINT cbPacket)
+{
+ /* Note: we're using KeGetCurrentProcessorNumber, which is not entirely correct in case
+ * we're running on 64bit win7+, which can handle > 64 CPUs, however since KeGetCurrentProcessorNumber
+ * always returns the number < than the number of CPUs in the first group, we're guaranteed to have CPU index < 64
+ * @todo: use KeGetCurrentProcessorNumberEx for Win7+ 64 and dynamically extended array */
+ ULONG Proc = KeGetCurrentProcessorNumber();
+ Assert(Proc < RT_ELEMENTS(pNetFlt->u.s.WinIf.abIndicateRxComplete));
+ pNetFlt->u.s.WinIf.abIndicateRxComplete[Proc] = TRUE;
+ switch (pNetFlt->u.s.WinIf.enmMedium)
+ {
+ case NdisMedium802_3:
+ case NdisMediumWan:
+ NdisMEthIndicateReceive(pNetFlt->u.s.WinIf.hMiniport,
+ MacReceiveContext,
+ (PCHAR)pHeaderBuffer,
+ cbHeaderBuffer,
+ pLookAheadBuffer,
+ cbLookAheadBuffer,
+ cbPacket);
+ break;
+ default:
+ AssertFailed();
+ break;
+ }
+}
+
+/**
+ * process the ProtocolReceive in an "active" mode
+ *
+ * @return NDIS_STATUS_SUCCESS - the packet is processed
+ * NDIS_STATUS_PENDING - the packet is being processed, we are waiting for the ProtocolTransferDataComplete to be called
+ * NDIS_STATUS_NOT_ACCEPTED - the packet is not needed - typically this is because this is a loopback packet
+ * NDIS_STATUS_FAILURE - packet processing failed
+ */
+static NDIS_STATUS vboxNetFltWinPtReceiveActive(PVBOXNETFLTINS pNetFlt, NDIS_HANDLE MacReceiveContext, PVOID pHeaderBuffer, UINT cbHeaderBuffer,
+ PVOID pLookaheadBuffer, UINT cbLookaheadBuffer, UINT cbPacket)
+{
+ NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
+
+ do
+ {
+ if (cbHeaderBuffer != VBOXNETFLT_PACKET_ETHEADER_SIZE)
+ {
+ Status = NDIS_STATUS_NOT_ACCEPTED;
+ break;
+ }
+
+#ifndef DEBUG_NETFLT_RECV_TRANSFERDATA
+ if (cbPacket == cbLookaheadBuffer)
+ {
+ PINTNETSG pSG;
+ PUCHAR pRcvData;
+#ifndef VBOX_LOOPBACK_USEFLAGS
+ PNDIS_PACKET pLb;
+#endif
+
+ /* allocate SG buffer */
+ Status = vboxNetFltWinAllocSG(cbPacket + cbHeaderBuffer, &pSG);
+ if (Status != NDIS_STATUS_SUCCESS)
+ {
+ AssertFailed();
+ break;
+ }
+
+ pRcvData = (PUCHAR)pSG->aSegs[0].pv;
+
+ NdisMoveMappedMemory(pRcvData, pHeaderBuffer, cbHeaderBuffer);
+
+ NdisCopyLookaheadData(pRcvData+cbHeaderBuffer,
+ pLookaheadBuffer,
+ cbLookaheadBuffer,
+ pNetFlt->u.s.WinIf.fMacOptions);
+#ifndef VBOX_LOOPBACK_USEFLAGS
+ pLb = vboxNetFltWinLbSearchLoopBackBySG(pNetFlt, pSG, false);
+ if (pLb)
+ {
+#ifndef DEBUG_NETFLT_RECV_NOPACKET
+ /* should not be here */
+ AssertFailed();
+#endif
+ if (!vboxNetFltWinLbIsFromIntNet(pLb))
+ {
+ PNDIS_PACKET pMyPacket;
+ pMyPacket = vboxNetFltWinNdisPacketFromSG(pNetFlt, /* PVBOXNETFLTINS */
+ pSG, /* PINTNETSG */
+ pSG, /* PVOID pBufToFree */
+ false, /* bool bToWire */
+ false); /* bool bCopyMemory */
+ if (pMyPacket)
+ {
+ NdisMIndicateReceivePacket(pNetFlt->u.s.WinIf.hMiniport, &pMyPacket, 1);
+ /* dereference the NetFlt here & indicate SUCCESS, which would mean the caller would not do a dereference
+ * the WinIf dereference will be done on packet return */
+ vboxNetFltWinDereferenceNetFlt(pNetFlt);
+ Status = NDIS_STATUS_SUCCESS;
+ }
+ else
+ {
+ vboxNetFltWinMemFree(pSG);
+ Status = NDIS_STATUS_FAILURE;
+ }
+ }
+ else
+ {
+ vboxNetFltWinMemFree(pSG);
+ Status = NDIS_STATUS_NOT_ACCEPTED;
+ }
+ break;
+ }
+#endif
+ VBOXNETFLT_LBVERIFYSG(pNetFlt, pSG);
+
+ /* enqueue SG */
+# ifdef VBOXNETFLT_NO_PACKET_QUEUE
+ if (vboxNetFltWinPostIntnet(pNetFlt, pSG, VBOXNETFLT_PACKET_SG))
+ {
+ /* drop it */
+ vboxNetFltWinMemFree(pSG);
+ vboxNetFltWinDereferenceWinIf(pNetFlt);
+ }
+ else
+ {
+ PNDIS_PACKET pMyPacket = vboxNetFltWinNdisPacketFromSG(pNetFlt, /* PVBOXNETFLTINS */
+ pSG, /* PINTNETSG */
+ pSG, /* PVOID pBufToFree */
+ false, /* bool bToWire */
+ false); /* bool bCopyMemory */
+ Assert(pMyPacket);
+ if (pMyPacket)
+ {
+ NDIS_SET_PACKET_STATUS(pMyPacket, NDIS_STATUS_SUCCESS);
+
+ DBG_CHECK_PACKET_AND_SG(pMyPacket, pSG);
+
+ LogFlow(("non-ndis packet info, packet created (%p)\n", pMyPacket));
+ NdisMIndicateReceivePacket(pNetFlt->u.s.WinIf.hMiniport, &pMyPacket, 1);
+ }
+ else
+ {
+ vboxNetFltWinDereferenceWinIf(pNetFlt);
+ Status = NDIS_STATUS_RESOURCES;
+ }
+ }
+ vboxNetFltWinDereferenceNetFlt(pNetFlt);
+# else
+ Status = vboxNetFltWinQuEnqueuePacket(pNetFlt, pSG, PACKET_SG | PACKET_MINE);
+ if (Status != NDIS_STATUS_SUCCESS)
+ {
+ AssertFailed();
+ vboxNetFltWinMemFree(pSG);
+ break;
+ }
+# endif
+#endif
+ }
+ else
+ {
+ PNDIS_PACKET pPacket;
+ PNDIS_BUFFER pTransferBuffer;
+ PNDIS_BUFFER pOrigBuffer;
+ PUCHAR pMemBuf;
+ UINT cbBuf = cbPacket + cbHeaderBuffer;
+ UINT cbTransferred;
+
+ /* allocate NDIS Packet buffer */
+ NdisAllocatePacket(&Status, &pPacket, pNetFlt->u.s.WinIf.hRecvPacketPool);
+ if (Status != NDIS_STATUS_SUCCESS)
+ {
+ AssertFailed();
+ break;
+ }
+
+ VBOXNETFLT_OOB_INIT(pPacket);
+
+#ifdef VBOX_LOOPBACK_USEFLAGS
+ /* set "don't loopback" flags */
+ NdisGetPacketFlags(pPacket) = g_VBoxNetFltGlobalsWin.fPacketDontLoopBack;
+#else
+ NdisGetPacketFlags(pPacket) = 0;
+#endif
+
+ Status = vboxNetFltWinMemAlloc((PVOID*)(&pMemBuf), cbBuf);
+ if (Status != NDIS_STATUS_SUCCESS)
+ {
+ AssertFailed();
+ NdisFreePacket(pPacket);
+ break;
+ }
+ NdisAllocateBuffer(&Status, &pTransferBuffer, pNetFlt->u.s.WinIf.hRecvBufferPool, pMemBuf + cbHeaderBuffer, cbPacket);
+ if (Status != NDIS_STATUS_SUCCESS)
+ {
+ AssertFailed();
+ Status = NDIS_STATUS_FAILURE;
+ NdisFreePacket(pPacket);
+ vboxNetFltWinMemFree(pMemBuf);
+ break;
+ }
+
+ NdisAllocateBuffer(&Status, &pOrigBuffer, pNetFlt->u.s.WinIf.hRecvBufferPool, pMemBuf, cbBuf);
+ if (Status != NDIS_STATUS_SUCCESS)
+ {
+ AssertFailed();
+ Status = NDIS_STATUS_FAILURE;
+ NdisFreeBuffer(pTransferBuffer);
+ NdisFreePacket(pPacket);
+ vboxNetFltWinMemFree(pMemBuf);
+ break;
+ }
+
+ NdisChainBufferAtBack(pPacket, pTransferBuffer);
+
+ NdisMoveMappedMemory(pMemBuf, pHeaderBuffer, cbHeaderBuffer);
+
+ vboxNetFltWinPutPacketToList(&pNetFlt->u.s.WinIf.TransferDataList, pPacket, pOrigBuffer);
+
+#ifdef DEBUG_NETFLT_RECV_TRANSFERDATA
+ if (cbPacket == cbLookaheadBuffer)
+ {
+ NdisCopyLookaheadData(pMemBuf+cbHeaderBuffer,
+ pLookaheadBuffer,
+ cbLookaheadBuffer,
+ pNetFlt->u.s.WinIf.fMacOptions);
+ }
+ else
+#endif
+ {
+ Assert(cbPacket > cbLookaheadBuffer);
+
+ NdisTransferData(&Status, pNetFlt->u.s.WinIf.hBinding, MacReceiveContext,
+ 0, /* ByteOffset */
+ cbPacket, pPacket, &cbTransferred);
+ }
+
+ if (Status != NDIS_STATUS_PENDING)
+ {
+ vboxNetFltWinPtTransferDataComplete(pNetFlt, pPacket, Status, cbTransferred);
+ }
+ }
+ } while (0);
+
+ return Status;
+}
+
+static NDIS_STATUS vboxNetFltWinPtReceive(IN NDIS_HANDLE hProtocolBindingContext,
+ IN NDIS_HANDLE MacReceiveContext,
+ IN PVOID pHeaderBuffer,
+ IN UINT cbHeaderBuffer,
+ IN PVOID pLookAheadBuffer,
+ IN UINT cbLookAheadBuffer,
+ IN UINT cbPacket)
+{
+ PVBOXNETFLTINS pNetFlt = (PVBOXNETFLTINS)hProtocolBindingContext;
+ PNDIS_PACKET pPacket = NULL;
+ NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
+ bool bNetFltActive;
+ bool fWinIfActive = vboxNetFltWinReferenceWinIfNetFlt(pNetFlt, &bNetFltActive);
+ const bool bPassThruActive = !bNetFltActive;
+
+ LogFlowFunc(("ENTER: pNetFlt (0x%p)\n", pNetFlt));
+
+ if (fWinIfActive)
+ {
+ do
+ {
+#ifndef DEBUG_NETFLT_RECV_NOPACKET
+ pPacket = NdisGetReceivedPacket(pNetFlt->u.s.WinIf.hBinding, MacReceiveContext);
+ if (pPacket)
+ {
+# ifndef VBOX_LOOPBACK_USEFLAGS
+ PNDIS_PACKET pLb = NULL;
+# else
+ if (vboxNetFltWinIsLoopedBackPacket(pPacket))
+ {
+ AssertFailed();
+ /* nothing else to do here, just return the packet */
+ //NdisReturnPackets(&pPacket, 1);
+ Status = NDIS_STATUS_NOT_ACCEPTED;
+ break;
+ }
+
+ VBOXNETFLT_LBVERIFY(pNetFlt, pPacket);
+# endif
+
+ if (bNetFltActive)
+ {
+# ifndef VBOX_LOOPBACK_USEFLAGS
+ pLb = vboxNetFltWinLbSearchLoopBack(pNetFlt, pPacket, false);
+ if (!pLb)
+# endif
+ {
+ VBOXNETFLT_LBVERIFY(pNetFlt, pPacket);
+
+# ifdef VBOXNETFLT_NO_PACKET_QUEUE
+ if (vboxNetFltWinPostIntnet(pNetFlt, pPacket, 0))
+ {
+ /* drop it */
+ break;
+ }
+# else
+ Status = vboxNetFltWinQuEnqueuePacket(pNetFlt, pPacket, PACKET_COPY);
+ Assert(Status == NDIS_STATUS_SUCCESS);
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ //NdisReturnPackets(&pPacket, 1);
+ fWinIfActive = false;
+ bNetFltActive = false;
+ break;
+ }
+# endif
+ }
+# ifndef VBOX_LOOPBACK_USEFLAGS
+ else if (vboxNetFltWinLbIsFromIntNet(pLb))
+ {
+ /* nothing else to do here, just return the packet */
+ //NdisReturnPackets(&pPacket, 1);
+ Status = NDIS_STATUS_NOT_ACCEPTED;
+ break;
+ }
+ /* we are here because this is a looped back packet set not from intnet
+ * we will post it to the upper protocol */
+# endif
+ }
+
+ Assert(Status == STATUS_SUCCESS);
+ if (Status == STATUS_SUCCESS)
+ {
+# ifndef VBOX_LOOPBACK_USEFLAGS
+ Assert(!pLb || !vboxNetFltWinLbIsFromIntNet(pLb));
+# endif
+ Status = vboxNetFltWinRecvPassThru(pNetFlt, pPacket);
+ Assert(Status == STATUS_SUCCESS);
+ /* we are done with packet processing, and we will
+ * not receive packet return event for this packet,
+ * fWinIfActive should be true to ensure we release WinIf*/
+ Assert(fWinIfActive);
+ if (Status == STATUS_SUCCESS)
+ break;
+ }
+ else
+ {
+ /* intnet processing failed - fall back to no-packet mode */
+ Assert(bNetFltActive);
+ Assert(fWinIfActive);
+ }
+
+ }
+#endif /* #ifndef DEBUG_NETFLT_RECV_NOPACKET */
+
+ if (bNetFltActive)
+ {
+ Status = vboxNetFltWinPtReceiveActive(pNetFlt, MacReceiveContext, pHeaderBuffer, cbHeaderBuffer,
+ pLookAheadBuffer, cbLookAheadBuffer, cbPacket);
+ if (NT_SUCCESS(Status))
+ {
+ if (Status != NDIS_STATUS_NOT_ACCEPTED)
+ {
+ fWinIfActive = false;
+ bNetFltActive = false;
+ }
+ else
+ {
+#ifndef VBOX_LOOPBACK_USEFLAGS
+ /* this is a loopback packet, nothing to do here */
+#else
+ AssertFailed();
+ /* should not be here */
+#endif
+ }
+ break;
+ }
+ }
+
+ /* we are done with packet processing, and we will
+ * not receive packet return event for this packet,
+ * fWinIfActive should be true to ensure we release WinIf*/
+ Assert(fWinIfActive);
+
+ vboxNetFltWinRecvIndicatePassThru(pNetFlt, MacReceiveContext, pHeaderBuffer, cbHeaderBuffer, pLookAheadBuffer, cbLookAheadBuffer, cbPacket);
+ /* the status could contain an error value here in case the IntNet recv failed,
+ * ensure we return back success status */
+ Status = NDIS_STATUS_SUCCESS;
+
+ } while (0);
+
+ if (bNetFltActive)
+ {
+ vboxNetFltWinDereferenceNetFlt(pNetFlt);
+ }
+ else if (bPassThruActive)
+ {
+ vboxNetFltWinDereferenceModePassThru(pNetFlt);
+ }
+ if (fWinIfActive)
+ {
+ vboxNetFltWinDereferenceWinIf(pNetFlt);
+ }
+ }
+ else
+ {
+ Status = NDIS_STATUS_FAILURE;
+ }
+
+ LogFlowFunc(("LEAVE: pNetFlt (0x%p)\n", pNetFlt));
+
+ return Status;
+
+}
+
+static VOID vboxNetFltWinPtReceiveComplete(NDIS_HANDLE hProtocolBindingContext)
+{
+ PVBOXNETFLTINS pNetFlt = (PVBOXNETFLTINS)hProtocolBindingContext;
+ bool fNetFltActive;
+ bool fWinIfActive = vboxNetFltWinReferenceWinIfNetFlt(pNetFlt, &fNetFltActive);
+ NDIS_HANDLE hMiniport = pNetFlt->u.s.WinIf.hMiniport;
+ /* Note: we're using KeGetCurrentProcessorNumber, which is not entirely correct in case
+ * we're running on 64bit win7+, which can handle > 64 CPUs, however since KeGetCurrentProcessorNumber
+ * always returns the number < than the number of CPUs in the first group, we're guaranteed to have CPU index < 64
+ * @todo: use KeGetCurrentProcessorNumberEx for Win7+ 64 and dynamically extended array */
+ ULONG iProc = KeGetCurrentProcessorNumber();
+ Assert(iProc < RT_ELEMENTS(pNetFlt->u.s.WinIf.abIndicateRxComplete));
+
+ LogFlowFunc(("ENTER: pNetFlt (0x%p)\n", pNetFlt));
+
+ if (hMiniport != NULL && pNetFlt->u.s.WinIf.abIndicateRxComplete[iProc])
+ {
+ switch (pNetFlt->u.s.WinIf.enmMedium)
+ {
+ case NdisMedium802_3:
+ case NdisMediumWan:
+ NdisMEthIndicateReceiveComplete(hMiniport);
+ break;
+ default:
+ AssertFailed();
+ break;
+ }
+ }
+
+ pNetFlt->u.s.WinIf.abIndicateRxComplete[iProc] = FALSE;
+
+ if (fWinIfActive)
+ {
+ if (fNetFltActive)
+ vboxNetFltWinDereferenceNetFlt(pNetFlt);
+ else
+ vboxNetFltWinDereferenceModePassThru(pNetFlt);
+ vboxNetFltWinDereferenceWinIf(pNetFlt);
+ }
+
+ LogFlowFunc(("LEAVE: pNetFlt (0x%p)\n", pNetFlt));
+}
+
+static INT vboxNetFltWinPtReceivePacket(NDIS_HANDLE hProtocolBindingContext, PNDIS_PACKET pPacket)
+{
+ PVBOXNETFLTINS pNetFlt = (PVBOXNETFLTINS)hProtocolBindingContext;
+ INT cRefCount = 0;
+ bool bNetFltActive;
+ bool fWinIfActive = vboxNetFltWinReferenceWinIfNetFlt(pNetFlt, &bNetFltActive);
+ const bool bPassThruActive = !bNetFltActive;
+
+ LogFlowFunc(("ENTER: pNetFlt (0x%p)\n", pNetFlt));
+
+ if (fWinIfActive)
+ {
+ do
+ {
+#ifdef VBOX_LOOPBACK_USEFLAGS
+ if (vboxNetFltWinIsLoopedBackPacket(pPacket))
+ {
+ AssertFailed();
+ Log(("lb_rp"));
+
+ /* nothing else to do here, just return the packet */
+ cRefCount = 0;
+ //NdisReturnPackets(&pPacket, 1);
+ break;
+ }
+
+ VBOXNETFLT_LBVERIFY(pNetFlt, pPacket);
+#endif
+
+ if (bNetFltActive)
+ {
+#ifndef VBOX_LOOPBACK_USEFLAGS
+ PNDIS_PACKET pLb = vboxNetFltWinLbSearchLoopBack(pNetFlt, pPacket, false);
+ if (!pLb)
+#endif
+ {
+#ifndef VBOXNETFLT_NO_PACKET_QUEUE
+ NDIS_STATUS fStatus;
+#endif
+ bool fResources = NDIS_GET_PACKET_STATUS(pPacket) == NDIS_STATUS_RESOURCES; NOREF(fResources);
+
+ VBOXNETFLT_LBVERIFY(pNetFlt, pPacket);
+#ifdef DEBUG_misha
+ /** @todo remove this assert.
+ * this is a temporary assert for debugging purposes:
+ * we're probably doing something wrong with the packets if the miniport reports NDIS_STATUS_RESOURCES */
+ Assert(!fResources);
+#endif
+
+#ifdef VBOXNETFLT_NO_PACKET_QUEUE
+ if (vboxNetFltWinPostIntnet(pNetFlt, pPacket, 0))
+ {
+ /* drop it */
+ cRefCount = 0;
+ break;
+ }
+
+#else
+ fStatus = vboxNetFltWinQuEnqueuePacket(pNetFlt, pPacket, fResources ? PACKET_COPY : 0);
+ if (fStatus == NDIS_STATUS_SUCCESS)
+ {
+ bNetFltActive = false;
+ fWinIfActive = false;
+ if (fResources)
+ {
+ cRefCount = 0;
+ //NdisReturnPackets(&pPacket, 1);
+ }
+ else
+ cRefCount = 1;
+ break;
+ }
+ else
+ {
+ AssertFailed();
+ }
+#endif
+ }
+#ifndef VBOX_LOOPBACK_USEFLAGS
+ else if (vboxNetFltWinLbIsFromIntNet(pLb))
+ {
+ /* the packet is from intnet, it has already been set to the host,
+ * no need for loopng it back to the host again */
+ /* nothing else to do here, just return the packet */
+ cRefCount = 0;
+ //NdisReturnPackets(&pPacket, 1);
+ break;
+ }
+#endif
+ }
+
+ cRefCount = vboxNetFltWinRecvPacketPassThru(pNetFlt, pPacket);
+ if (cRefCount)
+ {
+ Assert(cRefCount == 1);
+ fWinIfActive = false;
+ }
+
+ } while (FALSE);
+
+ if (bNetFltActive)
+ {
+ vboxNetFltWinDereferenceNetFlt(pNetFlt);
+ }
+ else if (bPassThruActive)
+ {
+ vboxNetFltWinDereferenceModePassThru(pNetFlt);
+ }
+ if (fWinIfActive)
+ {
+ vboxNetFltWinDereferenceWinIf(pNetFlt);
+ }
+ }
+ else
+ {
+ cRefCount = 0;
+ //NdisReturnPackets(&pPacket, 1);
+ }
+
+ LogFlowFunc(("LEAVE: pNetFlt (0x%p), cRefCount (%d)\n", pNetFlt, cRefCount));
+
+ return cRefCount;
+}
+
+DECLHIDDEN(bool) vboxNetFltWinPtCloseInterface(PVBOXNETFLTINS pNetFlt, PNDIS_STATUS pStatus)
+{
+ RTSpinlockAcquire(pNetFlt->hSpinlock);
+
+ if (pNetFlt->u.s.WinIf.StateFlags.fInterfaceClosing)
+ {
+ RTSpinlockRelease(pNetFlt->hSpinlock);
+ AssertFailed();
+ return false;
+ }
+ if (pNetFlt->u.s.WinIf.hBinding == NULL)
+ {
+ RTSpinlockRelease(pNetFlt->hSpinlock);
+ AssertFailed();
+ return false;
+ }
+
+ pNetFlt->u.s.WinIf.StateFlags.fInterfaceClosing = TRUE;
+ RTSpinlockRelease(pNetFlt->hSpinlock);
+
+ NdisResetEvent(&pNetFlt->u.s.WinIf.OpenCloseEvent);
+ NdisCloseAdapter(pStatus, pNetFlt->u.s.WinIf.hBinding);
+ if (*pStatus == NDIS_STATUS_PENDING)
+ {
+ NdisWaitEvent(&pNetFlt->u.s.WinIf.OpenCloseEvent, 0);
+ *pStatus = pNetFlt->u.s.WinIf.OpenCloseStatus;
+ }
+
+ Assert (*pStatus == NDIS_STATUS_SUCCESS);
+
+ pNetFlt->u.s.WinIf.hBinding = NULL;
+
+ return true;
+}
+
+static NDIS_STATUS vboxNetFltWinPtPnPSetPower(PVBOXNETFLTINS pNetFlt, NDIS_DEVICE_POWER_STATE enmPowerState)
+{
+ NDIS_DEVICE_POWER_STATE enmPrevPowerState = vboxNetFltWinGetPowerState(&pNetFlt->u.s.WinIf.PtState);
+
+ RTSpinlockAcquire(pNetFlt->hSpinlock);
+
+ vboxNetFltWinSetPowerState(&pNetFlt->u.s.WinIf.PtState, enmPowerState);
+
+ if (vboxNetFltWinGetPowerState(&pNetFlt->u.s.WinIf.PtState) > NdisDeviceStateD0)
+ {
+ if (enmPrevPowerState == NdisDeviceStateD0)
+ {
+ pNetFlt->u.s.WinIf.StateFlags.fStandBy = TRUE;
+ }
+ RTSpinlockRelease(pNetFlt->hSpinlock);
+ vboxNetFltWinPtRequestsWaitComplete(pNetFlt);
+ vboxNetFltWinWaitDereference(&pNetFlt->u.s.WinIf.MpState);
+ vboxNetFltWinWaitDereference(&pNetFlt->u.s.WinIf.PtState);
+
+ /* check packet pool is empty */
+ UINT cPPUsage = NdisPacketPoolUsage(pNetFlt->u.s.WinIf.hSendPacketPool);
+ Assert(cPPUsage == 0);
+ cPPUsage = NdisPacketPoolUsage(pNetFlt->u.s.WinIf.hRecvPacketPool);
+ Assert(cPPUsage == 0);
+ /* for debugging only, ignore the err in release */
+ NOREF(cPPUsage);
+
+ Assert(!pNetFlt->u.s.WinIf.StateFlags.fRequestInfo);
+ }
+ else
+ {
+ if (enmPrevPowerState > NdisDeviceStateD0)
+ {
+ pNetFlt->u.s.WinIf.StateFlags.fStandBy = FALSE;
+ }
+
+ if (pNetFlt->u.s.WinIf.StateFlags.fRequestInfo & VBOXNDISREQUEST_QUEUED)
+ {
+ pNetFlt->u.s.WinIf.StateFlags.fRequestInfo = VBOXNDISREQUEST_INPROGRESS;
+ RTSpinlockRelease(pNetFlt->hSpinlock);
+
+ vboxNetFltWinMpRequestPost(pNetFlt);
+ }
+ else
+ {
+ RTSpinlockRelease(pNetFlt->hSpinlock);
+ }
+ }
+
+ return NDIS_STATUS_SUCCESS;
+}
+
+
+static NDIS_STATUS vboxNetFltWinPtPnPEvent(IN NDIS_HANDLE hProtocolBindingContext, IN PNET_PNP_EVENT pNetPnPEvent)
+{
+ PVBOXNETFLTINS pNetFlt = (PVBOXNETFLTINS)hProtocolBindingContext;
+
+ LogFlowFunc(("ENTER: pNetFlt (0x%p), NetEvent (%d)\n", pNetFlt, pNetPnPEvent->NetEvent));
+
+ switch (pNetPnPEvent->NetEvent)
+ {
+ case NetEventSetPower:
+ {
+ NDIS_DEVICE_POWER_STATE enmPowerState = *((PNDIS_DEVICE_POWER_STATE)pNetPnPEvent->Buffer);
+ NDIS_STATUS rcNdis = vboxNetFltWinPtPnPSetPower(pNetFlt, enmPowerState);
+ LogFlowFunc(("LEAVE: pNetFlt (0x%p), NetEvent (%d), rcNdis=%#x\n", pNetFlt, pNetPnPEvent->NetEvent, rcNdis));
+ return rcNdis;
+ }
+
+ case NetEventReconfigure:
+ {
+ if (!pNetFlt)
+ {
+ NdisReEnumerateProtocolBindings(g_VBoxNetFltGlobalsWin.Pt.hProtocol);
+ }
+ }
+ /** @todo r=bird: Is the fall thru intentional?? */
+ default:
+ LogFlowFunc(("LEAVE: pNetFlt (0x%p), NetEvent (%d)\n", pNetFlt, pNetPnPEvent->NetEvent));
+ return NDIS_STATUS_SUCCESS;
+ }
+
+}
+
+#ifdef __cplusplus
+# define PTCHARS_40(_p) ((_p).Ndis40Chars)
+#else
+# define PTCHARS_40(_p) (_p)
+#endif
+
+/**
+ * register the protocol edge
+ */
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinPtRegister(PVBOXNETFLTGLOBALS_PT pGlobalsPt, PDRIVER_OBJECT pDriverObject,
+ PUNICODE_STRING pRegistryPathStr)
+{
+ RT_NOREF2(pDriverObject, pRegistryPathStr);
+ NDIS_PROTOCOL_CHARACTERISTICS PtChars;
+ NDIS_STRING NameStr;
+
+ NdisInitUnicodeString(&NameStr, VBOXNETFLT_NAME_PROTOCOL);
+
+ NdisZeroMemory(&PtChars, sizeof (PtChars));
+ PTCHARS_40(PtChars).MajorNdisVersion = VBOXNETFLT_VERSION_PT_NDIS_MAJOR;
+ PTCHARS_40(PtChars).MinorNdisVersion = VBOXNETFLT_VERSION_PT_NDIS_MINOR;
+
+ PTCHARS_40(PtChars).Name = NameStr;
+ PTCHARS_40(PtChars).OpenAdapterCompleteHandler = vboxNetFltWinPtOpenAdapterComplete;
+ PTCHARS_40(PtChars).CloseAdapterCompleteHandler = vboxNetFltWinPtCloseAdapterComplete;
+ PTCHARS_40(PtChars).SendCompleteHandler = vboxNetFltWinPtSendComplete;
+ PTCHARS_40(PtChars).TransferDataCompleteHandler = vboxNetFltWinPtTransferDataComplete;
+ PTCHARS_40(PtChars).ResetCompleteHandler = vboxNetFltWinPtResetComplete;
+ PTCHARS_40(PtChars).RequestCompleteHandler = vboxNetFltWinPtRequestComplete;
+ PTCHARS_40(PtChars).ReceiveHandler = vboxNetFltWinPtReceive;
+ PTCHARS_40(PtChars).ReceiveCompleteHandler = vboxNetFltWinPtReceiveComplete;
+ PTCHARS_40(PtChars).StatusHandler = vboxNetFltWinPtStatus;
+ PTCHARS_40(PtChars).StatusCompleteHandler = vboxNetFltWinPtStatusComplete;
+ PTCHARS_40(PtChars).BindAdapterHandler = vboxNetFltWinPtBindAdapter;
+ PTCHARS_40(PtChars).UnbindAdapterHandler = vboxNetFltWinPtUnbindAdapter;
+ PTCHARS_40(PtChars).UnloadHandler = vboxNetFltWinPtUnloadProtocol;
+#if !defined(DEBUG_NETFLT_RECV)
+ PTCHARS_40(PtChars).ReceivePacketHandler = vboxNetFltWinPtReceivePacket;
+#endif
+ PTCHARS_40(PtChars).PnPEventHandler = vboxNetFltWinPtPnPEvent;
+
+ NDIS_STATUS Status;
+ NdisRegisterProtocol(&Status, &pGlobalsPt->hProtocol, &PtChars, sizeof (PtChars));
+ Assert(Status == STATUS_SUCCESS);
+ return Status;
+}
+
+/**
+ * deregister the protocol edge
+ */
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinPtDeregister(PVBOXNETFLTGLOBALS_PT pGlobalsPt)
+{
+ if (!pGlobalsPt->hProtocol)
+ return NDIS_STATUS_SUCCESS;
+
+ NDIS_STATUS Status;
+
+ NdisDeregisterProtocol(&Status, pGlobalsPt->hProtocol);
+ Assert (Status == NDIS_STATUS_SUCCESS);
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ NdisZeroMemory(pGlobalsPt, sizeof (*pGlobalsPt));
+ }
+ return Status;
+}
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltP-win.h b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltP-win.h
new file mode 100644
index 00000000..11910676
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltP-win.h
@@ -0,0 +1,52 @@
+/* $Id: VBoxNetFltP-win.h $ */
+/** @file
+ * VBoxNetFltP-win.h - Bridged Networking Driver, Windows Specific Code.
+ * Protocol edge API
+ */
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_SRC_VBoxNetFlt_win_drv_VBoxNetFltP_win_h
+#define VBOX_INCLUDED_SRC_VBoxNetFlt_win_drv_VBoxNetFltP_win_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#ifdef VBOXNETADP
+# error "No protocol edge"
+#endif
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinPtRegister(PVBOXNETFLTGLOBALS_PT pGlobalsPt, PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegistryPathStr);
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinPtDeregister(PVBOXNETFLTGLOBALS_PT pGlobalsPt);
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinPtDoUnbinding(PVBOXNETFLTINS pNetFlt, bool bOnUnbind);
+DECLHIDDEN(VOID) vboxNetFltWinPtRequestComplete(NDIS_HANDLE hContext, PNDIS_REQUEST pNdisRequest, NDIS_STATUS Status);
+DECLHIDDEN(bool) vboxNetFltWinPtCloseInterface(PVBOXNETFLTINS pNetFlt, PNDIS_STATUS pStatus);
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinPtDoBinding(PVBOXNETFLTINS pThis, PNDIS_STRING pOurDeviceName, PNDIS_STRING pBindToDeviceName);
+#endif /* !VBOX_INCLUDED_SRC_VBoxNetFlt_win_drv_VBoxNetFltP_win_h */
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltRt-win.cpp b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltRt-win.cpp
new file mode 100644
index 00000000..44c7338c
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltRt-win.cpp
@@ -0,0 +1,3650 @@
+/* $Id: VBoxNetFltRt-win.cpp $ */
+/** @file
+ * VBoxNetFltRt-win.cpp - Bridged Networking Driver, Windows Specific Runtime Code.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "VBoxNetFltCmn-win.h"
+#include <VBox/intnetinline.h>
+#include <iprt/thread.h>
+
+#include <iprt/nt/tdikrnl.h>
+#include <mstcpip.h>
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/** represents the job element of the job queue
+ * see comments for VBOXNETFLT_JOB_QUEUE */
+typedef struct VBOXNETFLT_JOB
+{
+ /** link in the job queue */
+ LIST_ENTRY ListEntry;
+ /** job function to be executed */
+ PFNVBOXNETFLT_JOB_ROUTINE pfnRoutine;
+ /** parameter to be passed to the job function */
+ PVOID pContext;
+ /** event that will be fired on job completion */
+ KEVENT CompletionEvent;
+ /** true if the job manager should use the completion even for completion indication, false-otherwise*/
+ bool bUseCompletionEvent;
+} VBOXNETFLT_JOB, *PVBOXNETFLT_JOB;
+
+/**
+ * represents the queue of jobs processed by the worker thread
+ *
+ * we use the thread to process tasks which are required to be done at passive level
+ * our callbacks may be called at APC level by IntNet, there are some tasks that we can not create at APC,
+ * e.g. thread creation. This is why we schedule such jobs to the worker thread working at passive level
+ */
+typedef struct VBOXNETFLT_JOB_QUEUE
+{
+ /* jobs */
+ LIST_ENTRY Jobs;
+ /* we are using ExInterlocked..List functions to access the jobs list */
+ KSPIN_LOCK Lock;
+ /** this event is used to initiate a job worker thread kill */
+ KEVENT KillEvent;
+ /** this event is used to notify a worker thread that jobs are added to the queue */
+ KEVENT NotifyEvent;
+ /** worker thread */
+ PKTHREAD pThread;
+} VBOXNETFLT_JOB_QUEUE, *PVBOXNETFLT_JOB_QUEUE;
+
+typedef struct _CREATE_INSTANCE_CONTEXT
+{
+#ifndef VBOXNETADP
+ PNDIS_STRING pOurName;
+ PNDIS_STRING pBindToName;
+#else
+ NDIS_HANDLE hMiniportAdapter;
+ NDIS_HANDLE hWrapperConfigurationContext;
+#endif
+ NDIS_STATUS Status;
+} CREATE_INSTANCE_CONTEXT, *PCREATE_INSTANCE_CONTEXT;
+
+/*contexts used for our jobs */
+/* Attach context */
+typedef struct _ATTACH_INFO
+{
+ PVBOXNETFLTINS pNetFltIf;
+ PCREATE_INSTANCE_CONTEXT pCreateContext;
+ bool fRediscovery;
+ int Status;
+} ATTACH_INFO, *PATTACH_INFO;
+
+/* general worker context */
+typedef struct _WORKER_INFO
+{
+ PVBOXNETFLTINS pNetFltIf;
+ int Status;
+} WORKER_INFO, *PWORKER_INFO;
+
+/* idc initialization */
+typedef struct _INIT_IDC_INFO
+{
+ VBOXNETFLT_JOB Job;
+ bool bInitialized;
+ volatile bool bStop;
+ volatile int rc;
+ KEVENT hCompletionEvent;
+} INIT_IDC_INFO, *PINIT_IDC_INFO;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** global job queue. some operations are required to be done at passive level, e.g. thread creation, adapter bind/unbind initiation,
+ * while IntNet typically calls us APC_LEVEL, so we just create a system thread in our DriverEntry and enqueue the jobs to that thread */
+static VBOXNETFLT_JOB_QUEUE g_VBoxJobQueue;
+volatile static bool g_bVBoxIdcInitialized;
+INIT_IDC_INFO g_VBoxInitIdcInfo;
+
+/**
+ * The (common) global data.
+ */
+static VBOXNETFLTGLOBALS g_VBoxNetFltGlobals;
+/* win-specific global data */
+VBOXNETFLTGLOBALS_WIN g_VBoxNetFltGlobalsWin = {{{0}}};
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#define LIST_ENTRY_2_JOB(pListEntry) \
+ ( (PVBOXNETFLT_JOB)((uint8_t *)(pListEntry) - RT_UOFFSETOF(VBOXNETFLT_JOB, ListEntry)) )
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static int vboxNetFltWinAttachToInterface(PVBOXNETFLTINS pThis, void * pContext, bool fRediscovery);
+static int vboxNetFltWinConnectIt(PVBOXNETFLTINS pThis);
+static int vboxNetFltWinFiniIdc();
+static void vboxNetFltWinFiniNetFltBase();
+static int vboxNetFltWinInitNetFltBase();
+static int vboxNetFltWinFiniNetFlt();
+static int vboxNetFltWinStartInitIdcProbing();
+static int vboxNetFltWinStopInitIdcProbing();
+
+
+
+/** makes the current thread to sleep for the given number of miliseconds */
+DECLHIDDEN(void) vboxNetFltWinSleep(ULONG milis)
+{
+ RTThreadSleep(milis);
+}
+
+/** wait for the given device to be dereferenced */
+DECLHIDDEN(void) vboxNetFltWinWaitDereference(PVBOXNETFLT_WINIF_DEVICE pState)
+{
+#ifdef DEBUG
+ uint64_t StartNanoTS = RTTimeSystemNanoTS();
+ uint64_t CurNanoTS;
+#endif
+ Assert(KeGetCurrentIrql() < DISPATCH_LEVEL);
+
+ while (ASMAtomicUoReadU32((volatile uint32_t *)&pState->cReferences))
+ {
+ vboxNetFltWinSleep(2);
+#ifdef DEBUG
+ CurNanoTS = RTTimeSystemNanoTS();
+ if (CurNanoTS - StartNanoTS > 20000000)
+ {
+ LogRel(("device not idle"));
+ AssertFailed();
+// break;
+ }
+#endif
+ }
+}
+
+/**
+ * mem functions
+ */
+/* allocates and zeroes the nonpaged memory of a given size */
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinMemAlloc(PVOID *ppvMemBuf, UINT cbLength)
+{
+#ifdef DEBUG_NETFLT_USE_EXALLOC
+ *ppvMemBuf = ExAllocatePoolWithTag(NonPagedPool, cbLength, VBOXNETFLT_MEM_TAG);
+ if (*ppvMemBuf)
+ {
+ NdisZeroMemory(*ppvMemBuf, cbLength);
+ return NDIS_STATUS_SUCCESS;
+ }
+ return NDIS_STATUS_FAILURE;
+#else
+ NDIS_STATUS fStatus = NdisAllocateMemoryWithTag(ppvMemBuf, cbLength, VBOXNETFLT_MEM_TAG);
+ if (fStatus == NDIS_STATUS_SUCCESS)
+ NdisZeroMemory(*ppvMemBuf, cbLength);
+ return fStatus;
+#endif
+}
+
+/* frees memory allocated with vboxNetFltWinMemAlloc */
+DECLHIDDEN(void) vboxNetFltWinMemFree(PVOID pvMemBuf)
+{
+#ifdef DEBUG_NETFLT_USE_EXALLOC
+ ExFreePool(pvMemBuf);
+#else
+ NdisFreeMemory(pvMemBuf, 0, 0);
+#endif
+}
+
+#ifndef VBOXNETFLT_NO_PACKET_QUEUE
+
+/* initializes packet info pool and allocates the cSize packet infos for the pool */
+static NDIS_STATUS vboxNetFltWinPpAllocatePacketInfoPool(PVBOXNETFLT_PACKET_INFO_POOL pPool, UINT cSize)
+{
+ UINT cbBufSize = sizeof(PACKET_INFO)*cSize;
+ PACKET_INFO * pPacketInfos;
+ NDIS_STATUS fStatus;
+ UINT i;
+
+ Assert(cSize > 0);
+
+ INIT_INTERLOCKED_PACKET_QUEUE(&pPool->Queue);
+
+ fStatus = vboxNetFltWinMemAlloc((PVOID*)&pPacketInfos, cbBufSize);
+
+ if (fStatus == NDIS_STATUS_SUCCESS)
+ {
+ PVBOXNETFLTPACKET_INFO pInfo;
+ pPool->pBuffer = pPacketInfos;
+
+ for (i = 0; i < cSize; i++)
+ {
+ pInfo = &pPacketInfos[i];
+ vboxNetFltWinQuEnqueueTail(&pPool->Queue.Queue, pInfo);
+ pInfo->pPool = pPool;
+ }
+ }
+ else
+ {
+ AssertFailed();
+ }
+
+ return fStatus;
+}
+
+/* frees the packet info pool */
+VOID vboxNetFltWinPpFreePacketInfoPool(PVBOXNETFLT_PACKET_INFO_POOL pPool)
+{
+ vboxNetFltWinMemFree(pPool->pBuffer);
+
+ FINI_INTERLOCKED_PACKET_QUEUE(&pPool->Queue)
+}
+
+#endif
+
+/**
+ * copies one string to another. in case the destination string size is not enough to hold the complete source string
+ * does nothing and returns NDIS_STATUS_RESOURCES .
+ */
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinCopyString(PNDIS_STRING pDst, PNDIS_STRING pSrc)
+{
+ NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
+
+ if (pDst != pSrc)
+ {
+ if (pDst->MaximumLength < pSrc->Length)
+ {
+ AssertFailed();
+ Status = NDIS_STATUS_RESOURCES;
+ }
+ else
+ {
+ pDst->Length = pSrc->Length;
+
+ if (pDst->Buffer != pSrc->Buffer)
+ {
+ NdisMoveMemory(pDst->Buffer, pSrc->Buffer, pSrc->Length);
+ }
+ }
+ }
+ return Status;
+}
+
+/************************************************************************************
+ * PINTNETSG pSG manipulation functions
+ ************************************************************************************/
+
+/* moves the contents of the given NDIS_BUFFER and all other buffers chained to it to the PINTNETSG
+ * the PINTNETSG is expected to contain one segment whose bugger is large enough to maintain
+ * the contents of the given NDIS_BUFFER and all other buffers chained to it */
+static NDIS_STATUS vboxNetFltWinNdisBufferMoveToSG0(PNDIS_BUFFER pBuffer, PINTNETSG pSG)
+{
+ PINTNETSEG paSeg;
+ uint8_t * ptr;
+ PVOID pVirtualAddress;
+ UINT cbCurrentLength;
+ NDIS_STATUS fStatus = NDIS_STATUS_SUCCESS;
+
+ Assert(pSG->cSegsAlloc == 1);
+
+ paSeg = pSG->aSegs;
+ ptr = (uint8_t*)paSeg->pv;
+ paSeg->cb = 0;
+ paSeg->Phys = NIL_RTHCPHYS;
+ pSG->cbTotal = 0;
+
+ Assert(paSeg->pv);
+
+ while (pBuffer)
+ {
+ NdisQueryBufferSafe(pBuffer, &pVirtualAddress, &cbCurrentLength, NormalPagePriority);
+
+ if (!pVirtualAddress)
+ {
+ fStatus = NDIS_STATUS_FAILURE;
+ break;
+ }
+
+ pSG->cbTotal += cbCurrentLength;
+ paSeg->cb += cbCurrentLength;
+ NdisMoveMemory(ptr, pVirtualAddress, cbCurrentLength);
+ ptr += cbCurrentLength;
+
+ NdisGetNextBuffer(pBuffer, &pBuffer);
+ }
+
+ if (fStatus == NDIS_STATUS_SUCCESS)
+ {
+ pSG->cSegsUsed = 1;
+ Assert(pSG->cbTotal == paSeg->cb);
+ }
+ return fStatus;
+}
+
+/* converts the PNDIS_BUFFER to PINTNETSG by making the PINTNETSG segments to point to the memory buffers the
+ * ndis buffer(s) point to (as opposed to vboxNetFltWinNdisBufferMoveToSG0 which copies the memory from ndis buffers(s) to PINTNETSG) */
+static NDIS_STATUS vboxNetFltWinNdisBuffersToSG(PNDIS_BUFFER pBuffer, PINTNETSG pSG)
+{
+ UINT cSegs = 0;
+ NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
+ PVOID pVirtualAddress;
+ UINT cbCurrentLength;
+
+ while (pBuffer)
+ {
+ NdisQueryBufferSafe(pBuffer, &pVirtualAddress, &cbCurrentLength, NormalPagePriority);
+
+ if (!pVirtualAddress)
+ {
+ Status = NDIS_STATUS_FAILURE;
+ break;
+ }
+
+ pSG->cbTotal += cbCurrentLength;
+ pSG->aSegs[cSegs].cb = cbCurrentLength;
+ pSG->aSegs[cSegs].pv = pVirtualAddress;
+ pSG->aSegs[cSegs].Phys = NIL_RTHCPHYS;
+ cSegs++;
+
+ NdisGetNextBuffer(pBuffer, &pBuffer);
+ }
+
+ AssertFatal(cSegs <= pSG->cSegsAlloc);
+
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ pSG->cSegsUsed = cSegs;
+ }
+
+ return Status;
+}
+
+static void vboxNetFltWinDeleteSG(PINTNETSG pSG)
+{
+ vboxNetFltWinMemFree(pSG);
+}
+
+static PINTNETSG vboxNetFltWinCreateSG(uint32_t cSegs)
+{
+ PINTNETSG pSG;
+ NTSTATUS Status = vboxNetFltWinMemAlloc((PVOID*)&pSG, RT_UOFFSETOF_DYN(INTNETSG, aSegs[cSegs]));
+ if (Status == STATUS_SUCCESS)
+ {
+ IntNetSgInitTempSegs(pSG, 0 /*cbTotal*/, cSegs, 0 /*cSegsUsed*/);
+ return pSG;
+ }
+
+ return NULL;
+}
+
+/************************************************************************************
+ * packet queue functions
+ ************************************************************************************/
+#ifndef VBOXNETFLT_NO_PACKET_QUEUE
+#if !defined(VBOXNETADP)
+static NDIS_STATUS vboxNetFltWinQuPostPacket(PVBOXNETFLTINS pNetFlt, PNDIS_PACKET pPacket, PINTNETSG pSG, uint32_t fFlags
+# ifdef DEBUG_NETFLT_PACKETS
+ , PNDIS_PACKET pTmpPacket
+# endif
+ )
+{
+ NDIS_STATUS Status;
+ PNDIS_PACKET pMyPacket;
+ bool bSrcHost = fFlags & PACKET_SRC_HOST;
+
+ LogFlow(("posting packet back to driver stack..\n"));
+
+ if (!pPacket)
+ {
+ /* INTNETSG was in the packet queue, create a new NdisPacket from INTNETSG*/
+ pMyPacket = vboxNetFltWinNdisPacketFromSG(pNetFlt,
+ pSG, /* PINTNETSG */
+ pSG, /* PVOID pBufToFree */
+ bSrcHost, /* bool bToWire */
+ false); /* bool bCopyMemory */
+
+ Assert(pMyPacket);
+
+ NDIS_SET_PACKET_STATUS(pMyPacket, NDIS_STATUS_SUCCESS);
+
+ DBG_CHECK_PACKET_AND_SG(pMyPacket, pSG);
+
+#ifdef DEBUG_NETFLT_PACKETS
+ Assert(pTmpPacket);
+
+ DBG_CHECK_PACKET_AND_SG(pTmpPacket, pSG);
+
+ DBG_CHECK_PACKETS(pTmpPacket, pMyPacket);
+#endif
+
+ LogFlow(("non-ndis packet info, packet created (%p)\n", pMyPacket));
+ }
+ else
+ {
+ /* NDIS_PACKET was in the packet queue */
+ DBG_CHECK_PACKET_AND_SG(pPacket, pSG);
+
+ if (!(fFlags & PACKET_MINE))
+ {
+ /* the packet is the one that was passed to us in send/receive callback
+ * According to the DDK, we can not post it further,
+ * instead we should allocate our own packet.
+ * So, allocate our own packet (pMyPacket) and copy the packet info there */
+ if (bSrcHost)
+ {
+ Status = vboxNetFltWinPrepareSendPacket(pNetFlt, pPacket, &pMyPacket/*, true*/);
+ LogFlow(("packet from wire, packet created (%p)\n", pMyPacket));
+ }
+ else
+ {
+ Status = vboxNetFltWinPrepareRecvPacket(pNetFlt, pPacket, &pMyPacket, false);
+ LogFlow(("packet from wire, packet created (%p)\n", pMyPacket));
+ }
+ }
+ else
+ {
+ /* the packet enqueued is ours, simply assign pMyPacket and zero pPacket */
+ pMyPacket = pPacket;
+ pPacket = NULL;
+ }
+ Assert(pMyPacket);
+ }
+
+ if (pMyPacket)
+ {
+ /* we have successfully initialized our packet, post it to the host or to the wire */
+ if (bSrcHost)
+ {
+#if defined(DEBUG_NETFLT_PACKETS) || !defined(VBOX_LOOPBACK_USEFLAGS)
+ vboxNetFltWinLbPutSendPacket(pNetFlt, pMyPacket, false /* bFromIntNet */);
+#endif
+ NdisSend(&Status, pNetFlt->u.s.hBinding, pMyPacket);
+
+ if (Status != NDIS_STATUS_PENDING)
+ {
+#if defined(DEBUG_NETFLT_PACKETS) || !defined(VBOX_LOOPBACK_USEFLAGS)
+ /* the status is NOT pending, complete the packet */
+ bool bTmp = vboxNetFltWinLbRemoveSendPacket(pNetFlt, pMyPacket);
+ Assert(bTmp);
+#endif
+ if (pPacket)
+ {
+ LogFlow(("status is not pending, completing packet (%p)\n", pPacket));
+
+ NdisIMCopySendCompletePerPacketInfo (pPacket, pMyPacket);
+
+ NdisFreePacket(pMyPacket);
+ }
+ else
+ {
+ /* should never be here since the PINTNETSG is stored only when the underlying miniport
+ * indicates NDIS_STATUS_RESOURCES, we should never have this when processing
+ * the "from-host" packets */
+ AssertFailed();
+ LogFlow(("status is not pending, freeing myPacket (%p)\n", pMyPacket));
+ vboxNetFltWinFreeSGNdisPacket(pMyPacket, false);
+ }
+ }
+ }
+ else
+ {
+ NdisMIndicateReceivePacket(pNetFlt->u.s.hMiniport, &pMyPacket, 1);
+
+ Status = NDIS_STATUS_PENDING;
+ /* the packet receive completion is always indicated via MiniportReturnPacket */
+ }
+ }
+ else
+ {
+ /*we failed to create our packet */
+ AssertFailed();
+ Status = NDIS_STATUS_FAILURE;
+ }
+
+ return Status;
+}
+#endif
+
+static bool vboxNetFltWinQuProcessInfo(PVBOXNETFLTINS pNetFltIf, PPACKET_QUEUE_WORKER pWorker, PVOID pvPacket, const UINT fFlags)
+#else
+DECLHIDDEN(bool) vboxNetFltWinPostIntnet(PVBOXNETFLTINS pNetFltIf, PVOID pvPacket, const UINT fFlags)
+#endif
+{
+ PNDIS_PACKET pPacket = NULL;
+ PINTNETSG pSG = NULL;
+ NDIS_STATUS Status;
+#ifndef VBOXNETADP
+ bool bSrcHost;
+ bool bDropIt;
+# ifndef VBOXNETFLT_NO_PACKET_QUEUE
+ bool bPending;
+# endif
+#endif
+#ifdef VBOXNETFLT_NO_PACKET_QUEUE
+ bool bDeleteSG = false;
+#endif
+#ifdef DEBUG_NETFLT_PACKETS
+ /* packet used for matching */
+ PNDIS_PACKET pTmpPacket = NULL;
+#endif
+
+#ifndef VBOXNETADP
+ bSrcHost = (fFlags & VBOXNETFLT_PACKET_SRC_HOST) != 0;
+#endif
+
+ /* we first need to obtain the INTNETSG to be passed to intnet */
+
+ /* the queue may contain two "types" of packets:
+ * the NDIS_PACKET and the INTNETSG.
+ * I.e. on send/receive we typically enqueue the NDIS_PACKET passed to us by ndis,
+ * however in case our ProtocolReceive is called or the packet's status is set to NDIS_STSTUS_RESOURCES
+ * in ProtocolReceivePacket, we must return the packet immediately on ProtocolReceive*** exit
+ * In this case we allocate the INTNETSG, copy the ndis packet data there and enqueue it.
+ * In this case the packet info flags has the VBOXNETFLT_PACKET_SG fag set
+ *
+ * Besides that the NDIS_PACKET contained in the queue could be either the one passed to us in our send/receive callback
+ * or the one created by us. The latter is possible in case our ProtocolReceive callback is called and we call NdisTransferData
+ * in this case we need to allocate the packet the data to be transferred to.
+ * If the enqueued packet is the one allocated by us the VBOXNETFLT_PACKET_MINE flag is set
+ * */
+ if ((fFlags & VBOXNETFLT_PACKET_SG) == 0)
+ {
+ /* we have NDIS_PACKET enqueued, we need to convert it to INTNETSG to be passed to intnet */
+ PNDIS_BUFFER pCurrentBuffer = NULL;
+ UINT cBufferCount;
+ UINT cbPacketLength;
+
+ pPacket = (PNDIS_PACKET)pvPacket;
+
+ LogFlow(("ndis packet info, packet (%p)\n", pPacket));
+
+ LogFlow(("preparing pSG"));
+ NdisQueryPacket(pPacket, NULL, &cBufferCount, &pCurrentBuffer, &cbPacketLength);
+ Assert(cBufferCount);
+
+#ifdef VBOXNETFLT_NO_PACKET_QUEUE
+ pSG = vboxNetFltWinCreateSG(cBufferCount);
+#else
+ /* we can not allocate the INTNETSG on stack since in this case we may get stack overflow
+ * somewhere outside of our driver (3 pages of system thread stack does not seem to be enough)
+ *
+ * since we have a "serialized" packet processing, i.e. all packets are being processed and passed
+ * to intnet by this thread, we just use one previously allocated INTNETSG which is stored in PVBOXNETFLTINS */
+ pSG = pWorker->pSG;
+
+ if (cBufferCount > pSG->cSegsAlloc)
+ {
+ pSG = vboxNetFltWinCreateSG(cBufferCount + 2);
+ if (pSG)
+ {
+ vboxNetFltWinDeleteSG(pWorker->pSG);
+ pWorker->pSG = pSG;
+ }
+ else
+ {
+ LogRel(("Failed to reallocate the pSG\n"));
+ }
+ }
+#endif
+
+ if (pSG)
+ {
+#ifdef VBOXNETFLT_NO_PACKET_QUEUE
+ bDeleteSG = true;
+#endif
+ /* reinitialize */
+ IntNetSgInitTempSegs(pSG, 0 /*cbTotal*/, pSG->cSegsAlloc, 0 /*cSegsUsed*/);
+
+ /* convert the ndis buffers to INTNETSG */
+ Status = vboxNetFltWinNdisBuffersToSG(pCurrentBuffer, pSG);
+ if (Status != NDIS_STATUS_SUCCESS)
+ {
+ pSG = NULL;
+ }
+ else
+ {
+ DBG_CHECK_PACKET_AND_SG(pPacket, pSG);
+ }
+ }
+ }
+ else
+ {
+ /* we have the INTNETSG enqueued. (see the above comment explaining why/when this may happen)
+ * just use the INTNETSG to pass it to intnet */
+#ifndef VBOXNETADP
+ /* the PINTNETSG is stored only when the underlying miniport
+ * indicates NDIS_STATUS_RESOURCES, we should never have this when processing
+ * the "from-host" packedts */
+ Assert(!bSrcHost);
+#endif
+ pSG = (PINTNETSG)pvPacket;
+
+ LogFlow(("not ndis packet info, pSG (%p)\n", pSG));
+ }
+
+#ifdef DEBUG_NETFLT_PACKETS
+ if (!pPacket && !pTmpPacket)
+ {
+ /* create tmp packet that woud be used for matching */
+ pTmpPacket = vboxNetFltWinNdisPacketFromSG(pNetFltIf,
+ pSG, /* PINTNETSG */
+ pSG, /* PVOID pBufToFree */
+ bSrcHost, /* bool bToWire */
+ true); /* bool bCopyMemory */
+
+ NDIS_SET_PACKET_STATUS(pTmpPacket, NDIS_STATUS_SUCCESS);
+
+ DBG_CHECK_PACKET_AND_SG(pTmpPacket, pSG);
+
+ Assert(pTmpPacket);
+ }
+#endif
+ do
+ {
+#ifndef VBOXNETADP
+ /* the pSG was successfully initialized, post it to the netFlt*/
+ bDropIt = pSG ? pNetFltIf->pSwitchPort->pfnRecv(pNetFltIf->pSwitchPort, NULL /* pvIf */, pSG,
+ bSrcHost ? INTNETTRUNKDIR_HOST : INTNETTRUNKDIR_WIRE
+ )
+ : false;
+#else
+ if (pSG)
+ {
+ pNetFltIf->pSwitchPort->pfnRecv(pNetFltIf->pSwitchPort, NULL /* pvIf */, pSG, INTNETTRUNKDIR_HOST);
+ STATISTIC_INCREASE(pNetFltIf->u.s.WinIf.cTxSuccess);
+ }
+ else
+ {
+ STATISTIC_INCREASE(pNetFltIf->u.s.WinIf.cTxError);
+ }
+#endif
+
+#ifndef VBOXNETFLT_NO_PACKET_QUEUE
+
+# if !defined(VBOXNETADP)
+ if (!bDropIt)
+ {
+ Status = vboxNetFltWinQuPostPacket(pNetFltIf, pPacket, pSG, fFlags
+# ifdef DEBUG_NETFLT_PACKETS
+ , pTmpPacket
+# endif
+ );
+
+ if (Status == NDIS_STATUS_PENDING)
+ {
+ /* we will process packet completion in the completion routine */
+ bPending = true;
+ break;
+ }
+ }
+ else
+# endif
+ {
+ Status = NDIS_STATUS_SUCCESS;
+ }
+
+ /* drop it */
+ if (pPacket)
+ {
+ if (!(fFlags & PACKET_MINE))
+ {
+# if !defined(VBOXNETADP)
+ /* complete the packets */
+ if (fFlags & PACKET_SRC_HOST)
+ {
+# endif
+/* NDIS_SET_PACKET_STATUS(pPacket, Status); */
+ NdisMSendComplete(pNetFltIf->u.s.hMiniport, pPacket, Status);
+# if !defined(VBOXNETADP)
+ }
+ else
+ {
+# endif
+# ifndef VBOXNETADP
+ NdisReturnPackets(&pPacket, 1);
+# endif
+# if !defined(VBOXNETADP)
+ }
+# endif
+ }
+ else
+ {
+ Assert(!(fFlags & PACKET_SRC_HOST));
+ vboxNetFltWinFreeSGNdisPacket(pPacket, true);
+ }
+ }
+ else
+ {
+ Assert(pSG);
+ vboxNetFltWinMemFree(pSG);
+ }
+# ifndef VBOXNETADP
+ bPending = false;
+# endif
+ } while (0);
+
+#ifdef DEBUG_NETFLT_PACKETS
+ if (pTmpPacket)
+ {
+ vboxNetFltWinFreeSGNdisPacket(pTmpPacket, true);
+ }
+#endif
+
+#ifndef VBOXNETADP
+ return bPending;
+#else
+ return false;
+#endif
+#else /* #ifdef VBOXNETFLT_NO_PACKET_QUEUE */
+ } while (0);
+
+ if (bDeleteSG)
+ vboxNetFltWinMemFree(pSG);
+
+# ifndef VBOXNETADP
+ return bDropIt;
+# else
+ return true;
+# endif
+#endif
+}
+#ifndef VBOXNETFLT_NO_PACKET_QUEUE
+/*
+ * thread start function for the thread which processes the packets enqueued in our send and receive callbacks called by ndis
+ *
+ * ndis calls us at DISPATCH_LEVEL, while IntNet is using kernel functions which require Irql<DISPATCH_LEVEL
+ * this is why we can not immediately post packets to IntNet from our sen/receive callbacks
+ * instead we put the incoming packets to the queue and maintain the system thread running at passive level
+ * which processes the queue and posts the packets to IntNet, and further to the host or to the wire.
+ */
+static VOID vboxNetFltWinQuPacketQueueWorkerThreadProc(PVBOXNETFLTINS pNetFltIf)
+{
+ bool fResume = true;
+ NTSTATUS fStatus;
+ PPACKET_QUEUE_WORKER pWorker = &pNetFltIf->u.s.PacketQueueWorker;
+
+ PVOID apEvents[] = {
+ (PVOID)&pWorker->KillEvent,
+ (PVOID)&pWorker->NotifyEvent
+ };
+
+ while (fResume)
+ {
+ uint32_t cNumProcessed;
+ uint32_t cNumPostedToHostWire;
+
+ fStatus = KeWaitForMultipleObjects(RT_ELEMENTS(apEvents), apEvents, WaitAny, Executive, KernelMode, FALSE, NULL, NULL);
+ if (!NT_SUCCESS(fStatus) || fStatus == STATUS_WAIT_0)
+ {
+ /* "kill" event was set
+ * will process queued packets and exit */
+ fResume = false;
+ }
+
+ LogFlow(("processing vboxNetFltWinQuPacketQueueWorkerThreadProc\n"));
+
+ cNumProcessed = 0;
+ cNumPostedToHostWire = 0;
+
+ do
+ {
+ PVBOXNETFLTPACKET_INFO pInfo;
+
+#ifdef DEBUG_NETFLT_PACKETS
+ /* packet used for matching */
+ PNDIS_PACKET pTmpPacket = NULL;
+#endif
+
+ /** @todo FIXME: !!! the better approach for performance would be to dequeue all packets at once
+ * and then go through all dequeued packets
+ * the same should be done for enqueue !!! */
+ pInfo = vboxNetFltWinQuInterlockedDequeueHead(&pWorker->PacketQueue);
+
+ if (!pInfo)
+ {
+ break;
+ }
+
+ LogFlow(("found info (0x%p)\n", pInfo));
+
+ if (vboxNetFltWinQuProcessInfo(pNetFltIf, pWorker, pInfo->pPacket, pInfo->fFlags))
+ {
+ cNumPostedToHostWire++;
+ }
+
+ vboxNetFltWinPpFreePacketInfo(pInfo);
+
+ cNumProcessed++;
+ } while (TRUE);
+
+ if (cNumProcessed)
+ {
+ vboxNetFltWinDecReferenceNetFlt(pNetFltIf, cNumProcessed);
+
+ Assert(cNumProcessed >= cNumPostedToHostWire);
+
+ if (cNumProcessed != cNumPostedToHostWire)
+ {
+ vboxNetFltWinDecReferenceWinIf(pNetFltIf, cNumProcessed - cNumPostedToHostWire);
+ }
+ }
+ }
+
+ PsTerminateSystemThread(STATUS_SUCCESS);
+}
+#endif
+/**
+ * thread start function for the job processing thread
+ *
+ * see comments for PVBOXNETFLT_JOB_QUEUE
+ */
+static VOID vboxNetFltWinJobWorkerThreadProc(PVBOXNETFLT_JOB_QUEUE pQueue)
+{
+ bool fResume = true;
+ NTSTATUS Status;
+
+ PVOID apEvents[] = {
+ (PVOID)&pQueue->KillEvent,
+ (PVOID)&pQueue->NotifyEvent,
+ };
+
+ do
+ {
+ Status = KeWaitForMultipleObjects(RT_ELEMENTS(apEvents), apEvents, WaitAny, Executive, KernelMode, FALSE, NULL, NULL);
+ Assert(NT_SUCCESS(Status));
+ if (!NT_SUCCESS(Status) || Status == STATUS_WAIT_0)
+ {
+ /* will process queued jobs and exit */
+ Assert(Status == STATUS_WAIT_0);
+ fResume = false;
+ }
+
+ do
+ {
+ PLIST_ENTRY pJobEntry = ExInterlockedRemoveHeadList(&pQueue->Jobs, &pQueue->Lock);
+ PVBOXNETFLT_JOB pJob;
+
+ if (!pJobEntry)
+ break;
+
+ pJob = LIST_ENTRY_2_JOB(pJobEntry);
+
+ Assert(KeGetCurrentIrql() == PASSIVE_LEVEL);
+ pJob->pfnRoutine(pJob->pContext);
+ Assert(KeGetCurrentIrql() == PASSIVE_LEVEL);
+
+ if (pJob->bUseCompletionEvent)
+ {
+ KeSetEvent(&pJob->CompletionEvent, 1, FALSE);
+ }
+ } while (TRUE);
+ } while (fResume);
+
+ Assert(Status == STATUS_WAIT_0);
+
+ PsTerminateSystemThread(STATUS_SUCCESS);
+}
+
+/**
+ * enqueues the job to the job queue to be processed by the job worker thread
+ * see comments for PVBOXNETFLT_JOB_QUEUE
+ */
+static VOID vboxNetFltWinJobEnqueueJob(PVBOXNETFLT_JOB_QUEUE pQueue, PVBOXNETFLT_JOB pJob, bool bEnqueueHead)
+{
+ if (bEnqueueHead)
+ {
+ ExInterlockedInsertHeadList(&pQueue->Jobs, &pJob->ListEntry, &pQueue->Lock);
+ }
+ else
+ {
+ ExInterlockedInsertTailList(&pQueue->Jobs, &pJob->ListEntry, &pQueue->Lock);
+ }
+
+ KeSetEvent(&pQueue->NotifyEvent, 1, FALSE);
+}
+
+DECLINLINE(VOID) vboxNetFltWinJobInit(PVBOXNETFLT_JOB pJob, PFNVBOXNETFLT_JOB_ROUTINE pfnRoutine, PVOID pContext, bool bUseEvent)
+{
+ pJob->pfnRoutine = pfnRoutine;
+ pJob->pContext = pContext;
+ pJob->bUseCompletionEvent = bUseEvent;
+ if (bUseEvent)
+ KeInitializeEvent(&pJob->CompletionEvent, NotificationEvent, FALSE);
+}
+
+/**
+ * enqueues the job to the job queue to be processed by the job worker thread and
+ * blocks until the job is done
+ * see comments for PVBOXNETFLT_JOB_QUEUE
+ */
+static VOID vboxNetFltWinJobSynchExec(PVBOXNETFLT_JOB_QUEUE pQueue, PFNVBOXNETFLT_JOB_ROUTINE pfnRoutine, PVOID pContext)
+{
+ VBOXNETFLT_JOB Job;
+
+ Assert(KeGetCurrentIrql() < DISPATCH_LEVEL);
+
+ vboxNetFltWinJobInit(&Job, pfnRoutine, pContext, true);
+
+ vboxNetFltWinJobEnqueueJob(pQueue, &Job, false);
+
+ KeWaitForSingleObject(&Job.CompletionEvent, Executive, KernelMode, FALSE, NULL);
+}
+
+/**
+ * enqueues the job to be processed by the job worker thread at passive level and
+ * blocks until the job is done
+ */
+DECLHIDDEN(VOID) vboxNetFltWinJobSynchExecAtPassive(PFNVBOXNETFLT_JOB_ROUTINE pfnRoutine, PVOID pContext)
+{
+ vboxNetFltWinJobSynchExec(&g_VBoxJobQueue, pfnRoutine, pContext);
+}
+
+/**
+ * helper function used for system thread creation
+ */
+static NTSTATUS vboxNetFltWinQuCreateSystemThread(PKTHREAD *ppThread, PKSTART_ROUTINE pfnStartRoutine, PVOID pvStartContext)
+{
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ Assert(KeGetCurrentIrql() == PASSIVE_LEVEL);
+
+ InitializeObjectAttributes(&ObjectAttributes, NULL, OBJ_KERNEL_HANDLE, NULL, NULL);
+
+ HANDLE hThread;
+ NTSTATUS Status = PsCreateSystemThread(&hThread, THREAD_ALL_ACCESS, &ObjectAttributes, NULL, NULL, (PKSTART_ROUTINE)pfnStartRoutine, pvStartContext);
+ Assert(Status == STATUS_SUCCESS);
+ if (Status == STATUS_SUCCESS)
+ {
+ Status = ObReferenceObjectByHandle(hThread, THREAD_ALL_ACCESS, NULL, KernelMode, (PVOID*)ppThread, NULL);
+ Assert(Status == STATUS_SUCCESS);
+ ZwClose(hThread);
+ if (Status == STATUS_SUCCESS)
+ {
+ return STATUS_SUCCESS;
+ }
+
+ /** @todo how would we fail in this case ?*/
+ }
+ return Status;
+}
+
+/**
+ * initialize the job queue
+ * see comments for PVBOXNETFLT_JOB_QUEUE
+ */
+static NTSTATUS vboxNetFltWinJobInitQueue(PVBOXNETFLT_JOB_QUEUE pQueue)
+{
+ NTSTATUS fStatus;
+
+ Assert(KeGetCurrentIrql() == PASSIVE_LEVEL);
+
+ NdisZeroMemory(pQueue, sizeof(VBOXNETFLT_JOB_QUEUE));
+
+ KeInitializeEvent(&pQueue->KillEvent, NotificationEvent, FALSE);
+
+ KeInitializeEvent(&pQueue->NotifyEvent, SynchronizationEvent, FALSE);
+
+ InitializeListHead(&pQueue->Jobs);
+
+ fStatus = vboxNetFltWinQuCreateSystemThread(&pQueue->pThread, (PKSTART_ROUTINE)vboxNetFltWinJobWorkerThreadProc, pQueue);
+ if (fStatus != STATUS_SUCCESS)
+ {
+ pQueue->pThread = NULL;
+ }
+ else
+ {
+ Assert(pQueue->pThread);
+ }
+
+ return fStatus;
+}
+
+/**
+ * deinitialize the job queue
+ * see comments for PVBOXNETFLT_JOB_QUEUE
+ */
+static void vboxNetFltWinJobFiniQueue(PVBOXNETFLT_JOB_QUEUE pQueue)
+{
+ Assert(KeGetCurrentIrql() == PASSIVE_LEVEL);
+
+ if (pQueue->pThread)
+ {
+ KeSetEvent(&pQueue->KillEvent, 0, FALSE);
+
+ KeWaitForSingleObject(pQueue->pThread, Executive,
+ KernelMode, FALSE, NULL);
+ }
+}
+
+#ifndef VBOXNETFLT_NO_PACKET_QUEUE
+
+/**
+ * initializes the packet queue
+ * */
+DECLHIDDEN(NTSTATUS) vboxNetFltWinQuInitPacketQueue(PVBOXNETFLTINS pInstance)
+{
+ NTSTATUS Status;
+ PPACKET_QUEUE_WORKER pWorker = &pInstance->u.s.PacketQueueWorker;
+
+ AssertFatal(!pWorker->pSG);
+
+ Assert(KeGetCurrentIrql() == PASSIVE_LEVEL);
+
+ KeInitializeEvent(&pWorker->KillEvent, NotificationEvent, FALSE);
+
+ KeInitializeEvent(&pWorker->NotifyEvent, SynchronizationEvent, FALSE);
+
+ INIT_INTERLOCKED_PACKET_QUEUE(&pWorker->PacketQueue);
+
+ do
+ {
+ Status = vboxNetFltWinPpAllocatePacketInfoPool(&pWorker->PacketInfoPool, VBOXNETFLT_PACKET_INFO_POOL_SIZE);
+
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ pWorker->pSG = vboxNetFltWinCreateSG(PACKET_QUEUE_SG_SEGS_ALLOC);
+ if (!pWorker->pSG)
+ {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ break;
+ }
+
+ Status = vboxNetFltWinQuCreateSystemThread(&pWorker->pThread, (PKSTART_ROUTINE)vboxNetFltWinQuPacketQueueWorkerThreadProc, pInstance);
+ if (Status != STATUS_SUCCESS)
+ {
+ vboxNetFltWinPpFreePacketInfoPool(&pWorker->PacketInfoPool);
+ vboxNetFltWinMemFree(pWorker->pSG);
+ pWorker->pSG = NULL;
+ break;
+ }
+ }
+
+ } while (0);
+
+ return Status;
+}
+
+/*
+ * deletes the packet queue
+ */
+DECLHIDDEN(void) vboxNetFltWinQuFiniPacketQueue(PVBOXNETFLTINS pInstance)
+{
+ PINTNETSG pSG;
+ PPACKET_QUEUE_WORKER pWorker = &pInstance->u.s.PacketQueueWorker;
+ Assert(KeGetCurrentIrql() < DISPATCH_LEVEL);
+
+ /* using the pPacketQueueSG as an indicator that the packet queue is initialized */
+ RTSpinlockAcquire((pInstance)->hSpinlock);
+ if (pWorker->pSG)
+ {
+ pSG = pWorker->pSG;
+ pWorker->pSG = NULL;
+ RTSpinlockRelease((pInstance)->hSpinlock);
+ KeSetEvent(&pWorker->KillEvent, 0, FALSE);
+
+ KeWaitForSingleObject(pWorker->pThread, Executive,
+ KernelMode, FALSE, NULL);
+
+ vboxNetFltWinPpFreePacketInfoPool(&pWorker->PacketInfoPool);
+
+ vboxNetFltWinDeleteSG(pSG);
+
+ FINI_INTERLOCKED_PACKET_QUEUE(&pWorker->PacketQueue);
+ }
+ else
+ {
+ RTSpinlockRelease((pInstance)->hSpinlock);
+ }
+}
+
+#endif
+
+/*
+ * creates the INTNETSG containing one segment pointing to the buffer of size cbBufSize
+ * the INTNETSG created should be cleaned with vboxNetFltWinMemFree
+ */
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinAllocSG(UINT cbPacket, PINTNETSG *ppSG)
+{
+ NDIS_STATUS Status;
+ PINTNETSG pSG;
+
+ /* allocation:
+ * 1. SG_PACKET - with one aSegs pointing to
+ * 2. buffer of cbPacket containing the entire packet */
+ AssertCompileSizeAlignment(INTNETSG, sizeof(PVOID));
+ Status = vboxNetFltWinMemAlloc((PVOID*)&pSG, cbPacket + sizeof(INTNETSG));
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ IntNetSgInitTemp(pSG, pSG + 1, cbPacket);
+ LogFlow(("pSG created (%p)\n", pSG));
+ *ppSG = pSG;
+ }
+ return Status;
+}
+
+#ifndef VBOXNETFLT_NO_PACKET_QUEUE
+/**
+ * put the packet info to the queue
+ */
+DECLINLINE(void) vboxNetFltWinQuEnqueueInfo(PVBOXNETFLTPACKET_QUEUE_WORKER pWorker, PVBOXNETFLTPACKET_INFO pInfo)
+{
+ vboxNetFltWinQuInterlockedEnqueueTail(&pWorker->PacketQueue, pInfo);
+
+ KeSetEvent(&pWorker->NotifyEvent, IO_NETWORK_INCREMENT, FALSE);
+}
+
+/**
+ * puts the packet to the queue
+ *
+ * @return NDIST_STATUS_SUCCESS iff the packet was enqueued successfully
+ * and error status otherwise.
+ * NOTE: that the success status does NOT mean that the packet processing is completed, but only that it was enqueued successfully
+ * the packet can be returned to the caller protocol/moniport only in case the bReleasePacket was set to true (in this case the copy of the packet was enqueued)
+ * or if vboxNetFltWinQuEnqueuePacket failed, i.e. the packet was NOT enqueued
+ */
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinQuEnqueuePacket(PVBOXNETFLTINS pInstance, PVOID pPacket, const UINT fPacketFlags)
+{
+ PVBOXNETFLT_PACKET_INFO pInfo;
+ PVBOXNETFLT_PACKET_QUEUE_WORKER pWorker = &pInstance->u.s.PacketQueueWorker;
+ NDIS_STATUS fStatus = NDIS_STATUS_SUCCESS;
+
+ do
+ {
+ if (fPacketFlags & PACKET_COPY)
+ {
+ PNDIS_BUFFER pBuffer = NULL;
+ UINT cBufferCount;
+ UINT uBytesCopied = 0;
+ UINT cbPacketLength;
+ PINTNETSG pSG;
+
+ /* the packet is Ndis packet */
+ Assert(!(fPacketFlags & PACKET_SG));
+ Assert(!(fPacketFlags & PACKET_MINE));
+
+ NdisQueryPacket((PNDIS_PACKET)pPacket,
+ NULL,
+ &cBufferCount,
+ &pBuffer,
+ &cbPacketLength);
+
+
+ Assert(cBufferCount);
+
+ fStatus = vboxNetFltWinAllocSG(cbPacketLength, &pSG);
+ if (fStatus != NDIS_STATUS_SUCCESS)
+ {
+ AssertFailed();
+ break;
+ }
+
+ pInfo = vboxNetFltWinPpAllocPacketInfo(&pWorker->PacketInfoPool);
+
+ if (!pInfo)
+ {
+ AssertFailed();
+ /** @todo what status to set? */
+ fStatus = NDIS_STATUS_FAILURE;
+ vboxNetFltWinMemFree(pSG);
+ break;
+ }
+
+ Assert(pInfo->pPool);
+
+ /* the packet we are queueing is SG, add PACKET_SG to flags */
+ SET_FLAGS_TO_INFO(pInfo, fPacketFlags | PACKET_SG);
+ SET_PACKET_TO_INFO(pInfo, pSG);
+
+ fStatus = vboxNetFltWinNdisBufferMoveToSG0(pBuffer, pSG);
+ if (fStatus != NDIS_STATUS_SUCCESS)
+ {
+ AssertFailed();
+ vboxNetFltWinPpFreePacketInfo(pInfo);
+ vboxNetFltWinMemFree(pSG);
+ break;
+ }
+
+ DBG_CHECK_PACKET_AND_SG((PNDIS_PACKET)pPacket, pSG);
+ }
+ else
+ {
+ pInfo = vboxNetFltWinPpAllocPacketInfo(&pWorker->PacketInfoPool);
+
+ if (!pInfo)
+ {
+ AssertFailed();
+ /** @todo what status to set? */
+ fStatus = NDIS_STATUS_FAILURE;
+ break;
+ }
+
+ Assert(pInfo->pPool);
+
+ SET_FLAGS_TO_INFO(pInfo, fPacketFlags);
+ SET_PACKET_TO_INFO(pInfo, pPacket);
+ }
+
+ vboxNetFltWinQuEnqueueInfo(pWorker, pInfo);
+
+ } while (0);
+
+ return fStatus;
+}
+#endif
+
+
+/*
+ * netflt
+ */
+#ifndef VBOXNETADP
+static NDIS_STATUS vboxNetFltWinSynchNdisRequest(PVBOXNETFLTINS pNetFlt, PNDIS_REQUEST pRequest)
+{
+ int rc;
+
+ Assert(KeGetCurrentIrql() < DISPATCH_LEVEL);
+
+ /* 1. serialize */
+ rc = RTSemFastMutexRequest(pNetFlt->u.s.WinIf.hSynchRequestMutex); AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ NDIS_STATUS fRequestStatus = NDIS_STATUS_SUCCESS;
+
+ /* 2. set pNetFlt->u.s.pSynchRequest */
+ Assert(!pNetFlt->u.s.WinIf.pSynchRequest);
+ pNetFlt->u.s.WinIf.pSynchRequest = pRequest;
+
+ /* 3. call NdisRequest */
+ NdisRequest(&fRequestStatus, pNetFlt->u.s.WinIf.hBinding, pRequest);
+
+ if (fRequestStatus == NDIS_STATUS_PENDING)
+ {
+ /* 3.1 if pending wait and assign the resulting status */
+ KeWaitForSingleObject(&pNetFlt->u.s.WinIf.hSynchCompletionEvent, Executive,
+ KernelMode, FALSE, NULL);
+
+ fRequestStatus = pNetFlt->u.s.WinIf.SynchCompletionStatus;
+ }
+
+ /* 4. clear the pNetFlt->u.s.pSynchRequest */
+ pNetFlt->u.s.WinIf.pSynchRequest = NULL;
+
+ RTSemFastMutexRelease(pNetFlt->u.s.WinIf.hSynchRequestMutex); AssertRC(rc);
+ return fRequestStatus;
+ }
+ return NDIS_STATUS_FAILURE;
+}
+
+
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinGetMacAddress(PVBOXNETFLTINS pNetFlt, PRTMAC pMac)
+{
+ NDIS_REQUEST request;
+ NDIS_STATUS status;
+ request.RequestType = NdisRequestQueryInformation;
+ request.DATA.QUERY_INFORMATION.InformationBuffer = pMac;
+ request.DATA.QUERY_INFORMATION.InformationBufferLength = sizeof(RTMAC);
+ request.DATA.QUERY_INFORMATION.Oid = OID_802_3_CURRENT_ADDRESS;
+ status = vboxNetFltWinSynchNdisRequest(pNetFlt, &request);
+ if (status != NDIS_STATUS_SUCCESS)
+ {
+ /** @todo */
+ AssertFailed();
+ }
+
+ return status;
+
+}
+
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinQueryPhysicalMedium(PVBOXNETFLTINS pNetFlt, NDIS_PHYSICAL_MEDIUM * pMedium)
+{
+ NDIS_REQUEST Request;
+ NDIS_STATUS Status;
+ Request.RequestType = NdisRequestQueryInformation;
+ Request.DATA.QUERY_INFORMATION.InformationBuffer = pMedium;
+ Request.DATA.QUERY_INFORMATION.InformationBufferLength = sizeof(NDIS_PHYSICAL_MEDIUM);
+ Request.DATA.QUERY_INFORMATION.Oid = OID_GEN_PHYSICAL_MEDIUM;
+ Status = vboxNetFltWinSynchNdisRequest(pNetFlt, &Request);
+ if (Status != NDIS_STATUS_SUCCESS)
+ {
+ if (Status == NDIS_STATUS_NOT_SUPPORTED || Status == NDIS_STATUS_NOT_RECOGNIZED || Status == NDIS_STATUS_INVALID_OID)
+ {
+ Status = NDIS_STATUS_NOT_SUPPORTED;
+ }
+ else
+ {
+ LogRel(("OID_GEN_PHYSICAL_MEDIUM failed: Status (0x%x)", Status));
+ AssertFailed();
+ }
+ }
+ return Status;
+}
+
+DECLHIDDEN(bool) vboxNetFltWinIsPromiscuous(PVBOXNETFLTINS pNetFlt)
+{
+ /** @todo r=bird: This is too slow and is probably returning the wrong
+ * information. What we're interested in is whether someone besides us
+ * has put the interface into promiscuous mode. */
+ NDIS_REQUEST request;
+ NDIS_STATUS status;
+ ULONG filter;
+ Assert(VBOXNETFLT_PROMISCUOUS_SUPPORTED(pNetFlt));
+ request.RequestType = NdisRequestQueryInformation;
+ request.DATA.QUERY_INFORMATION.InformationBuffer = &filter;
+ request.DATA.QUERY_INFORMATION.InformationBufferLength = sizeof(filter);
+ request.DATA.QUERY_INFORMATION.Oid = OID_GEN_CURRENT_PACKET_FILTER;
+ status = vboxNetFltWinSynchNdisRequest(pNetFlt, &request);
+ if (status != NDIS_STATUS_SUCCESS)
+ {
+ /** @todo */
+ AssertFailed();
+ return false;
+ }
+ return (filter & NDIS_PACKET_TYPE_PROMISCUOUS) != 0;
+}
+
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinSetPromiscuous(PVBOXNETFLTINS pNetFlt, bool bYes)
+{
+/** @todo Need to report changes to the switch via:
+ * pThis->pSwitchPort->pfnReportPromiscuousMode(pThis->pSwitchPort, fPromisc);
+ */
+ Assert(VBOXNETFLT_PROMISCUOUS_SUPPORTED(pNetFlt));
+ if (VBOXNETFLT_PROMISCUOUS_SUPPORTED(pNetFlt))
+ {
+ NDIS_REQUEST Request;
+ NDIS_STATUS fStatus;
+ ULONG fFilter;
+ ULONG fExpectedFilter;
+ ULONG fOurFilter;
+ Request.RequestType = NdisRequestQueryInformation;
+ Request.DATA.QUERY_INFORMATION.InformationBuffer = &fFilter;
+ Request.DATA.QUERY_INFORMATION.InformationBufferLength = sizeof(fFilter);
+ Request.DATA.QUERY_INFORMATION.Oid = OID_GEN_CURRENT_PACKET_FILTER;
+ fStatus = vboxNetFltWinSynchNdisRequest(pNetFlt, &Request);
+ if (fStatus != NDIS_STATUS_SUCCESS)
+ {
+ /** @todo */
+ AssertFailed();
+ return fStatus;
+ }
+
+ if (!pNetFlt->u.s.WinIf.StateFlags.fUpperProtSetFilterInitialized)
+ {
+ /* the cache was not initialized yet, initiate it with the current filter value */
+ pNetFlt->u.s.WinIf.fUpperProtocolSetFilter = fFilter;
+ pNetFlt->u.s.WinIf.StateFlags.fUpperProtSetFilterInitialized = TRUE;
+ }
+
+
+ if (bYes)
+ {
+ fExpectedFilter = NDIS_PACKET_TYPE_PROMISCUOUS;
+ fOurFilter = NDIS_PACKET_TYPE_PROMISCUOUS;
+ }
+ else
+ {
+ fExpectedFilter = pNetFlt->u.s.WinIf.fUpperProtocolSetFilter;
+ fOurFilter = 0;
+ }
+
+ if (fExpectedFilter != fFilter)
+ {
+ Request.RequestType = NdisRequestSetInformation;
+ Request.DATA.SET_INFORMATION.InformationBuffer = &fExpectedFilter;
+ Request.DATA.SET_INFORMATION.InformationBufferLength = sizeof(fExpectedFilter);
+ Request.DATA.SET_INFORMATION.Oid = OID_GEN_CURRENT_PACKET_FILTER;
+ fStatus = vboxNetFltWinSynchNdisRequest(pNetFlt, &Request);
+ if (fStatus != NDIS_STATUS_SUCCESS)
+ {
+ /** @todo */
+ AssertFailed();
+ return fStatus;
+ }
+ }
+ pNetFlt->u.s.WinIf.fOurSetFilter = fOurFilter;
+ return fStatus;
+ }
+ return NDIS_STATUS_NOT_SUPPORTED;
+}
+
+#else /* VBOXNETADP */
+
+/**
+ * Generates a new unique MAC address based on our vendor ID
+ */
+DECLHIDDEN(void) vboxNetFltWinGenerateMACAddress(RTMAC *pMac)
+{
+ /* temporary use a time info */
+ uint64_t NanoTS = RTTimeSystemNanoTS();
+ pMac->au8[0] = (uint8_t)((VBOXNETADP_VENDOR_ID >> 16) & 0xff);
+ pMac->au8[1] = (uint8_t)((VBOXNETADP_VENDOR_ID >> 8) & 0xff);
+ pMac->au8[2] = (uint8_t)(VBOXNETADP_VENDOR_ID & 0xff);
+ pMac->au8[3] = (uint8_t)(NanoTS & 0xff0000);
+ pMac->au16[2] = (uint16_t)(NanoTS & 0xffff);
+}
+
+DECLHIDDEN(int) vboxNetFltWinMAC2NdisString(RTMAC *pMac, PNDIS_STRING pNdisString)
+{
+ static const char s_achDigits[17] = "0123456789abcdef";
+ PWSTR pString;
+
+ /* validate parameters */
+ AssertPtrReturn(pMac, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pNdisString, VERR_INVALID_PARAMETER);
+ AssertReturn(pNdisString->MaximumLength >= 13*sizeof(pNdisString->Buffer[0]), VERR_INVALID_PARAMETER);
+
+ pString = pNdisString->Buffer;
+
+ for (int i = 0; i < 6; i++)
+ {
+ uint8_t u8 = pMac->au8[i];
+ pString[0] = s_achDigits[(u8 >> 4) & 0xf];
+ pString[1] = s_achDigits[(u8/*>>0*/)& 0xf];
+ pString += 2;
+ }
+
+ pNdisString->Length = 12*sizeof(pNdisString->Buffer[0]);
+
+ *pString = L'\0';
+
+ return VINF_SUCCESS;
+}
+
+static int vboxNetFltWinWchar2Byte(WCHAR c, uint8_t *pb)
+{
+ if (c >= L'A' && c <= L'F')
+ *pb = (c - L'A') + 10;
+ else if (c >= L'a' && c <= L'f')
+ *pb = (c - L'a') + 10;
+ else if (c >= L'0' && c <= L'9')
+ *pb = (c - L'0');
+ else
+ return VERR_INVALID_PARAMETER;
+ return VINF_SUCCESS;
+}
+
+DECLHIDDEN(int) vboxNetFltWinMACFromNdisString(RTMAC *pMac, PNDIS_STRING pNdisString)
+{
+
+ /* validate parameters */
+ AssertPtrReturn(pMac, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pNdisString, VERR_INVALID_PARAMETER);
+ AssertReturn(pNdisString->Length >= 12*sizeof(pNdisString->Buffer[0]), VERR_INVALID_PARAMETER);
+
+ int rc = VINF_SUCCESS;
+ PWSTR pString = pNdisString->Buffer;
+ for (int i = 0; i < 6; i++)
+ {
+ uint8_t v1, v2;
+ rc = vboxNetFltWinWchar2Byte(pString[0], &v1);
+ if (RT_FAILURE(rc))
+ break;
+
+ rc = vboxNetFltWinWchar2Byte(pString[1], &v2);
+ if (RT_FAILURE(rc))
+ break;
+
+ pMac->au8[i] = (v1 << 4) | v2;
+
+ pString += 2;
+ }
+
+ return rc;
+}
+
+#endif /* VBOXNETADP */
+
+/**
+ * creates a NDIS_PACKET from the PINTNETSG
+ */
+DECLHIDDEN(PNDIS_PACKET) vboxNetFltWinNdisPacketFromSG(PVBOXNETFLTINS pNetFlt, PINTNETSG pSG, PVOID pBufToFree, bool bToWire, bool bCopyMemory)
+{
+ NDIS_STATUS fStatus;
+ PNDIS_PACKET pPacket;
+
+ Assert(pSG->aSegs[0].pv);
+ Assert(pSG->cbTotal >= VBOXNETFLT_PACKET_ETHEADER_SIZE);
+
+/** @todo Hrmpf, how can we fix this assumption? I fear this'll cause data
+ * corruption and maybe even BSODs ... */
+ AssertReturn(pSG->cSegsUsed == 1 || bCopyMemory, NULL);
+
+#ifdef VBOXNETADP
+ NdisAllocatePacket(&fStatus, &pPacket, pNetFlt->u.s.WinIf.hRecvPacketPool);
+#else
+ NdisAllocatePacket(&fStatus, &pPacket, bToWire ? pNetFlt->u.s.WinIf.hSendPacketPool : pNetFlt->u.s.WinIf.hRecvPacketPool);
+#endif
+ if (fStatus == NDIS_STATUS_SUCCESS)
+ {
+ PNDIS_BUFFER pBuffer;
+ PVOID pvMemBuf;
+
+ /** @todo generally we do not always need to zero-initialize the complete OOB data here, reinitialize only when/what we need,
+ * however we DO need to reset the status for the packets we indicate via NdisMIndicateReceivePacket to avoid packet loss
+ * in case the status contains NDIS_STATUS_RESOURCES */
+ VBOXNETFLT_OOB_INIT(pPacket);
+
+ if (bCopyMemory)
+ {
+ fStatus = vboxNetFltWinMemAlloc(&pvMemBuf, pSG->cbTotal);
+ Assert(fStatus == NDIS_STATUS_SUCCESS);
+ if (fStatus == NDIS_STATUS_SUCCESS)
+ IntNetSgRead(pSG, pvMemBuf);
+ }
+ else
+ {
+ pvMemBuf = pSG->aSegs[0].pv;
+ }
+ if (fStatus == NDIS_STATUS_SUCCESS)
+ {
+#ifdef VBOXNETADP
+ NdisAllocateBuffer(&fStatus, &pBuffer,
+ pNetFlt->u.s.WinIf.hRecvBufferPool,
+ pvMemBuf,
+ pSG->cbTotal);
+#else
+ NdisAllocateBuffer(&fStatus, &pBuffer,
+ bToWire ? pNetFlt->u.s.WinIf.hSendBufferPool : pNetFlt->u.s.WinIf.hRecvBufferPool,
+ pvMemBuf,
+ pSG->cbTotal);
+#endif
+
+ if (fStatus == NDIS_STATUS_SUCCESS)
+ {
+ NdisChainBufferAtBack(pPacket, pBuffer);
+
+ if (bToWire)
+ {
+ PVBOXNETFLT_PKTRSVD_PT pSendInfo = (PVBOXNETFLT_PKTRSVD_PT)pPacket->ProtocolReserved;
+ pSendInfo->pOrigPacket = NULL;
+ pSendInfo->pBufToFree = pBufToFree;
+#ifdef VBOX_LOOPBACK_USEFLAGS
+ /* set "don't loopback" flags */
+ NdisGetPacketFlags(pPacket) = g_VBoxNetFltGlobalsWin.fPacketDontLoopBack;
+#else
+ NdisGetPacketFlags(pPacket) = 0;
+#endif
+ }
+ else
+ {
+ PVBOXNETFLT_PKTRSVD_MP pRecvInfo = (PVBOXNETFLT_PKTRSVD_MP)pPacket->MiniportReserved;
+ pRecvInfo->pOrigPacket = NULL;
+ pRecvInfo->pBufToFree = pBufToFree;
+
+ /* we must set the header size on receive */
+ NDIS_SET_PACKET_HEADER_SIZE(pPacket, VBOXNETFLT_PACKET_ETHEADER_SIZE);
+ /* NdisAllocatePacket zero-initializes the OOB data,
+ * but keeps the packet flags, clean them here */
+ NdisGetPacketFlags(pPacket) = 0;
+ }
+ /** @todo set out of bound data */
+ }
+ else
+ {
+ AssertFailed();
+ if (bCopyMemory)
+ {
+ vboxNetFltWinMemFree(pvMemBuf);
+ }
+ NdisFreePacket(pPacket);
+ pPacket = NULL;
+ }
+ }
+ else
+ {
+ AssertFailed();
+ NdisFreePacket(pPacket);
+ pPacket = NULL;
+ }
+ }
+ else
+ {
+ pPacket = NULL;
+ }
+
+ DBG_CHECK_PACKET_AND_SG(pPacket, pSG);
+
+ return pPacket;
+}
+
+/*
+ * frees NDIS_PACKET created with vboxNetFltWinNdisPacketFromSG
+ */
+DECLHIDDEN(void) vboxNetFltWinFreeSGNdisPacket(PNDIS_PACKET pPacket, bool bFreeMem)
+{
+ UINT cBufCount;
+ PNDIS_BUFFER pFirstBuffer;
+ UINT uTotalPacketLength;
+ PNDIS_BUFFER pBuffer;
+
+ NdisQueryPacket(pPacket, NULL, &cBufCount, &pFirstBuffer, &uTotalPacketLength);
+
+ Assert(cBufCount == 1);
+
+ do
+ {
+ NdisUnchainBufferAtBack(pPacket, &pBuffer);
+ if (pBuffer != NULL)
+ {
+ PVOID pvMemBuf;
+ UINT cbLength;
+
+ NdisQueryBufferSafe(pBuffer, &pvMemBuf, &cbLength, NormalPagePriority);
+ NdisFreeBuffer(pBuffer);
+ if (bFreeMem)
+ {
+ vboxNetFltWinMemFree(pvMemBuf);
+ }
+ }
+ else
+ {
+ break;
+ }
+ } while (true);
+
+ NdisFreePacket(pPacket);
+}
+
+#if !defined(VBOXNETADP)
+static void vboxNetFltWinAssociateMiniportProtocol(PVBOXNETFLTGLOBALS_WIN pGlobalsWin)
+{
+ NdisIMAssociateMiniport(pGlobalsWin->Mp.hMiniport, pGlobalsWin->Pt.hProtocol);
+}
+#endif
+
+/*
+ * NetFlt driver unload function
+ */
+DECLHIDDEN(VOID) vboxNetFltWinUnload(IN PDRIVER_OBJECT DriverObject)
+{
+ int rc;
+ UNREFERENCED_PARAMETER(DriverObject);
+
+ LogFlowFunc(("ENTER: DO (0x%x)\n", DriverObject));
+
+ rc = vboxNetFltWinFiniIdc();
+ if (RT_FAILURE(rc))
+ {
+ /** @todo we can not prevent driver unload here */
+ AssertFailed();
+
+ LogFlowFunc(("vboxNetFltWinFiniIdc - failed, busy.\n"));
+ }
+
+ vboxNetFltWinJobFiniQueue(&g_VBoxJobQueue);
+#ifndef VBOXNETADP
+ vboxNetFltWinPtDeregister(&g_VBoxNetFltGlobalsWin.Pt);
+#endif
+
+ vboxNetFltWinMpDeregister(&g_VBoxNetFltGlobalsWin.Mp);
+
+#ifndef VBOXNETADP
+ NdisFreeSpinLock(&g_VBoxNetFltGlobalsWin.lockFilters);
+#endif /* VBOXNETADP */
+
+ LogFlow(("LEAVE: DO (0x%x)\n", DriverObject));
+
+ vboxNetFltWinFiniNetFltBase();
+ /* don't use logging or any RT after de-init */
+}
+
+RT_C_DECLS_BEGIN
+
+NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath);
+
+RT_C_DECLS_END
+
+NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
+{
+ NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
+ int rc;
+
+ /* the idc registration is initiated via IOCTL since our driver
+ * can be loaded when the VBoxDrv is not in case we are a Ndis IM driver */
+ rc = vboxNetFltWinInitNetFltBase();
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ Status = vboxNetFltWinJobInitQueue(&g_VBoxJobQueue);
+ Assert(Status == STATUS_SUCCESS);
+ if (Status == STATUS_SUCCESS)
+ {
+ ULONG MjVersion;
+ ULONG MnVersion;
+
+ /* note: we do it after we initialize the Job Queue */
+ vboxNetFltWinStartInitIdcProbing();
+
+ NdisZeroMemory(&g_VBoxNetFltGlobalsWin, sizeof (g_VBoxNetFltGlobalsWin));
+ KeInitializeEvent(&g_VBoxNetFltGlobalsWin.SynchEvent, SynchronizationEvent, TRUE /* signalled*/);
+
+ PsGetVersion(&MjVersion, &MnVersion,
+ NULL, /* PULONG BuildNumber OPTIONAL */
+ NULL /* PUNICODE_STRING CSDVersion OPTIONAL */
+ );
+
+ g_VBoxNetFltGlobalsWin.fPacketDontLoopBack = NDIS_FLAGS_DONT_LOOPBACK;
+
+ if (MjVersion == 5 && MnVersion == 0)
+ {
+ /* this is Win2k, we don't support it actually, but just in case */
+ g_VBoxNetFltGlobalsWin.fPacketDontLoopBack |= NDIS_FLAGS_SKIP_LOOPBACK_W2K;
+ }
+
+ g_VBoxNetFltGlobalsWin.fPacketIsLoopedBack = NDIS_FLAGS_IS_LOOPBACK_PACKET;
+
+#ifndef VBOXNETADP
+ RTListInit(&g_VBoxNetFltGlobalsWin.listFilters);
+ NdisAllocateSpinLock(&g_VBoxNetFltGlobalsWin.lockFilters);
+#endif
+
+ Status = vboxNetFltWinMpRegister(&g_VBoxNetFltGlobalsWin.Mp, DriverObject, RegistryPath);
+ Assert(Status == STATUS_SUCCESS);
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+#ifndef VBOXNETADP
+ Status = vboxNetFltWinPtRegister(&g_VBoxNetFltGlobalsWin.Pt, DriverObject, RegistryPath);
+ Assert(Status == STATUS_SUCCESS);
+ if (Status == NDIS_STATUS_SUCCESS)
+#endif
+ {
+#ifndef VBOXNETADP
+ vboxNetFltWinAssociateMiniportProtocol(&g_VBoxNetFltGlobalsWin);
+#endif
+ return STATUS_SUCCESS;
+
+//#ifndef VBOXNETADP
+// vboxNetFltWinPtDeregister(&g_VBoxNetFltGlobalsWin.Pt);
+//#endif
+ }
+#ifndef VBOXNETADP /* unreachable for VBOXNETADP because of the above return */
+ vboxNetFltWinMpDeregister(&g_VBoxNetFltGlobalsWin.Mp);
+# ifndef VBOXNETADP
+ NdisFreeSpinLock(&g_VBoxNetFltGlobalsWin.lockFilters);
+# endif
+#endif
+ }
+ vboxNetFltWinJobFiniQueue(&g_VBoxJobQueue);
+ }
+ vboxNetFltWinFiniNetFlt();
+ }
+ else
+ {
+ Status = NDIS_STATUS_FAILURE;
+ }
+
+ return Status;
+}
+
+#ifndef VBOXNETADP
+/**
+ * creates and initializes the packet to be sent to the underlying miniport given a packet posted to our miniport edge
+ * according to DDK docs we must create our own packet rather than posting the one passed to us
+ */
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinPrepareSendPacket(PVBOXNETFLTINS pNetFlt, PNDIS_PACKET pPacket, PNDIS_PACKET *ppMyPacket)
+{
+ NDIS_STATUS Status;
+
+ NdisAllocatePacket(&Status, ppMyPacket, pNetFlt->u.s.WinIf.hSendPacketPool);
+
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ PVBOXNETFLT_PKTRSVD_PT pSendInfo = (PVBOXNETFLT_PKTRSVD_PT)((*ppMyPacket)->ProtocolReserved);
+ pSendInfo->pOrigPacket = pPacket;
+ pSendInfo->pBufToFree = NULL;
+ /* the rest will be filled on send */
+
+ vboxNetFltWinCopyPacketInfoOnSend(*ppMyPacket, pPacket);
+
+#ifdef VBOX_LOOPBACK_USEFLAGS
+ NdisGetPacketFlags(*ppMyPacket) |= g_VBoxNetFltGlobalsWin.fPacketDontLoopBack;
+#endif
+ }
+ else
+ {
+ *ppMyPacket = NULL;
+ }
+
+ return Status;
+}
+
+/**
+ * creates and initializes the packet to be sent to the upperlying protocol given a packet indicated to our protocol edge
+ * according to DDK docs we must create our own packet rather than posting the one passed to us
+ */
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinPrepareRecvPacket(PVBOXNETFLTINS pNetFlt, PNDIS_PACKET pPacket, PNDIS_PACKET *ppMyPacket, bool bDpr)
+{
+ NDIS_STATUS Status;
+
+ if (bDpr)
+ {
+ Assert(KeGetCurrentIrql() == DISPATCH_LEVEL);
+ NdisDprAllocatePacket(&Status, ppMyPacket, pNetFlt->u.s.WinIf.hRecvPacketPool);
+ }
+ else
+ {
+ NdisAllocatePacket(&Status, ppMyPacket, pNetFlt->u.s.WinIf.hRecvPacketPool);
+ }
+
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ PVBOXNETFLT_PKTRSVD_MP pRecvInfo = (PVBOXNETFLT_PKTRSVD_MP)((*ppMyPacket)->MiniportReserved);
+ pRecvInfo->pOrigPacket = pPacket;
+ pRecvInfo->pBufToFree = NULL;
+
+ Status = vboxNetFltWinCopyPacketInfoOnRecv(*ppMyPacket, pPacket, false);
+ }
+ else
+ {
+ *ppMyPacket = NULL;
+ }
+ return Status;
+}
+#endif
+/**
+ * initializes the VBOXNETFLTINS (our context structure) and binds to the given adapter
+ */
+#if defined(VBOXNETADP)
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinPtInitBind(PVBOXNETFLTINS *ppNetFlt, NDIS_HANDLE hMiniportAdapter, PNDIS_STRING pBindToMiniportName /* actually this is our miniport name*/, NDIS_HANDLE hWrapperConfigurationContext)
+#else
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinPtInitBind(PVBOXNETFLTINS *ppNetFlt, PNDIS_STRING pOurMiniportName, PNDIS_STRING pBindToMiniportName)
+#endif
+{
+ NDIS_STATUS Status;
+ do
+ {
+ ANSI_STRING AnsiString;
+ int rc;
+ PVBOXNETFLTINS pInstance;
+ USHORT cbAnsiName = pBindToMiniportName->Length;/* the length is is bytes ; *2 ;RtlUnicodeStringToAnsiSize(pBindToMiniportName)*/
+ CREATE_INSTANCE_CONTEXT Context;
+
+# ifndef VBOXNETADP
+ Context.pOurName = pOurMiniportName;
+ Context.pBindToName = pBindToMiniportName;
+# else
+ Context.hMiniportAdapter = hMiniportAdapter;
+ Context.hWrapperConfigurationContext = hWrapperConfigurationContext;
+# endif
+ Context.Status = NDIS_STATUS_SUCCESS;
+
+ AnsiString.Buffer = 0; /* will be allocated by RtlUnicodeStringToAnsiString */
+ AnsiString.Length = 0;
+ AnsiString.MaximumLength = cbAnsiName;
+
+ Assert(KeGetCurrentIrql() == PASSIVE_LEVEL);
+
+ Status = RtlUnicodeStringToAnsiString(&AnsiString, pBindToMiniportName, true);
+
+ if (Status != STATUS_SUCCESS)
+ {
+ break;
+ }
+
+ rc = vboxNetFltSearchCreateInstance(&g_VBoxNetFltGlobals, AnsiString.Buffer, &pInstance, &Context);
+ RtlFreeAnsiString(&AnsiString);
+ if (RT_FAILURE(rc))
+ {
+ AssertFailed();
+ Status = Context.Status != NDIS_STATUS_SUCCESS ? Context.Status : NDIS_STATUS_FAILURE;
+ break;
+ }
+
+ Assert(pInstance);
+
+ if (rc == VINF_ALREADY_INITIALIZED)
+ {
+ /* the case when our adapter was unbound while IntNet was connected to it */
+ /* the instance remains valid until IntNet disconnects from it, we simply search and re-use it*/
+ rc = vboxNetFltWinAttachToInterface(pInstance, &Context, true);
+ if (RT_FAILURE(rc))
+ {
+ AssertFailed();
+ Status = Context.Status != NDIS_STATUS_SUCCESS ? Context.Status : NDIS_STATUS_FAILURE;
+ /* release netflt */
+ vboxNetFltRelease(pInstance, false);
+
+ break;
+ }
+ }
+
+ *ppNetFlt = pInstance;
+
+ } while (FALSE);
+
+ return Status;
+}
+/*
+ * deinitializes the VBOXNETFLTWIN
+ */
+DECLHIDDEN(VOID) vboxNetFltWinPtFiniWinIf(PVBOXNETFLTWIN pWinIf)
+{
+#ifndef VBOXNETADP
+ int rc;
+#endif
+
+ LogFlowFunc(("ENTER: pWinIf 0x%p\n", pWinIf));
+
+ Assert(KeGetCurrentIrql() == PASSIVE_LEVEL);
+#ifndef VBOXNETADP
+ if (pWinIf->MpDeviceName.Buffer)
+ {
+ vboxNetFltWinMemFree(pWinIf->MpDeviceName.Buffer);
+ }
+
+ FINI_INTERLOCKED_SINGLE_LIST(&pWinIf->TransferDataList);
+# if defined(DEBUG_NETFLT_LOOPBACK) || !defined(VBOX_LOOPBACK_USEFLAGS)
+ FINI_INTERLOCKED_SINGLE_LIST(&pWinIf->SendPacketQueue);
+# endif
+ NdisFreeBufferPool(pWinIf->hSendBufferPool);
+ NdisFreePacketPool(pWinIf->hSendPacketPool);
+ rc = RTSemFastMutexDestroy(pWinIf->hSynchRequestMutex); AssertRC(rc);
+#endif
+
+ /* NOTE: NULL is a valid handle */
+ NdisFreeBufferPool(pWinIf->hRecvBufferPool);
+ NdisFreePacketPool(pWinIf->hRecvPacketPool);
+
+ LogFlowFunc(("LEAVE: pWinIf 0x%p\n", pWinIf));
+}
+
+#ifndef VBOXNETADP
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinPtInitWinIf(PVBOXNETFLTWIN pWinIf, IN PNDIS_STRING pOurDeviceName)
+#else
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinPtInitWinIf(PVBOXNETFLTWIN pWinIf)
+#endif
+{
+ NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
+#ifndef VBOXNETADP
+ int rc;
+#endif
+
+ LogFlowFunc(("ENTER: pWinIf 0x%p\n", pWinIf));
+
+ Assert(KeGetCurrentIrql() == PASSIVE_LEVEL);
+
+ NdisZeroMemory(pWinIf, sizeof (VBOXNETFLTWIN));
+ NdisAllocatePacketPoolEx(&Status, &pWinIf->hRecvPacketPool,
+ VBOXNETFLT_PACKET_POOL_SIZE_NORMAL,
+ VBOXNETFLT_PACKET_POOL_SIZE_OVERFLOW,
+ PROTOCOL_RESERVED_SIZE_IN_PACKET);
+ Assert(Status == NDIS_STATUS_SUCCESS);
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ /* NOTE: NULL is a valid handle !!! */
+ NdisAllocateBufferPool(&Status, &pWinIf->hRecvBufferPool, VBOXNETFLT_BUFFER_POOL_SIZE_RX);
+ Assert(Status == NDIS_STATUS_SUCCESS);
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ pWinIf->MpState.PowerState = NdisDeviceStateD3;
+ vboxNetFltWinSetOpState(&pWinIf->MpState, kVBoxNetDevOpState_Deinitialized);
+#ifndef VBOXNETADP
+ pWinIf->PtState.PowerState = NdisDeviceStateD3;
+ vboxNetFltWinSetOpState(&pWinIf->PtState, kVBoxNetDevOpState_Deinitialized);
+
+ NdisAllocateBufferPool(&Status,
+ &pWinIf->hSendBufferPool,
+ VBOXNETFLT_BUFFER_POOL_SIZE_TX);
+ Assert(Status == NDIS_STATUS_SUCCESS);
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ INIT_INTERLOCKED_SINGLE_LIST(&pWinIf->TransferDataList);
+
+# if defined(DEBUG_NETFLT_LOOPBACK) || !defined(VBOX_LOOPBACK_USEFLAGS)
+ INIT_INTERLOCKED_SINGLE_LIST(&pWinIf->SendPacketQueue);
+# endif
+ NdisInitializeEvent(&pWinIf->OpenCloseEvent);
+
+ KeInitializeEvent(&pWinIf->hSynchCompletionEvent, SynchronizationEvent, FALSE);
+
+ NdisInitializeEvent(&pWinIf->MpInitCompleteEvent);
+
+ NdisAllocatePacketPoolEx(&Status, &pWinIf->hSendPacketPool,
+ VBOXNETFLT_PACKET_POOL_SIZE_NORMAL,
+ VBOXNETFLT_PACKET_POOL_SIZE_OVERFLOW,
+ sizeof (PVBOXNETFLT_PKTRSVD_PT));
+ Assert(Status == NDIS_STATUS_SUCCESS);
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ rc = RTSemFastMutexCreate(&pWinIf->hSynchRequestMutex);
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ Status = vboxNetFltWinMemAlloc((PVOID*)&pWinIf->MpDeviceName.Buffer, pOurDeviceName->Length);
+ Assert(Status == NDIS_STATUS_SUCCESS);
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ pWinIf->MpDeviceName.MaximumLength = pOurDeviceName->Length;
+ pWinIf->MpDeviceName.Length = 0;
+ Status = vboxNetFltWinCopyString(&pWinIf->MpDeviceName, pOurDeviceName);
+#endif
+ return NDIS_STATUS_SUCCESS;
+#ifndef VBOXNETADP
+ // unreachable: vboxNetFltWinMemFree(pWinIf->MpDeviceName.Buffer);
+ }
+ RTSemFastMutexDestroy(pWinIf->hSynchRequestMutex);
+ }
+ else
+ Status = NDIS_STATUS_FAILURE;
+ NdisFreePacketPool(pWinIf->hSendPacketPool);
+ }
+ NdisFreeBufferPool(pWinIf->hSendBufferPool);
+ }
+ NdisFreeBufferPool(pWinIf->hRecvBufferPool);
+#endif
+ }
+ NdisFreePacketPool(pWinIf->hRecvPacketPool);
+ }
+
+ LogFlowFunc(("LEAVE: pWinIf 0x%p, Status 0x%x\n", pWinIf, Status));
+
+ return Status;
+}
+
+/**
+ * match packets
+ */
+#define NEXT_LIST_ENTRY(_Entry) ((_Entry)->Flink)
+#define PREV_LIST_ENTRY(_Entry) ((_Entry)->Blink)
+#define FIRST_LIST_ENTRY NEXT_LIST_ENTRY
+#define LAST_LIST_ENTRY PREV_LIST_ENTRY
+
+#define MIN(_a, _b) ((_a) < (_b) ? (_a) : (_b))
+
+#ifndef VBOXNETADP
+
+#ifdef DEBUG_misha
+
+RTMAC g_vboxNetFltWinVerifyMACBroadcast = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+RTMAC g_vboxNetFltWinVerifyMACGuest = {0x08, 0x00, 0x27, 0x01, 0x02, 0x03};
+
+DECLHIDDEN(PRTNETETHERHDR) vboxNetFltWinGetEthHdr(PNDIS_PACKET pPacket)
+{
+ UINT cBufCount1;
+ PNDIS_BUFFER pBuffer1;
+ UINT uTotalPacketLength1;
+ RTNETETHERHDR* pEth;
+ UINT cbLength1 = 0;
+ UINT i = 0;
+
+ NdisQueryPacket(pPacket, NULL, &cBufCount1, &pBuffer1, &uTotalPacketLength1);
+
+ Assert(pBuffer1);
+ Assert(uTotalPacketLength1 >= VBOXNETFLT_PACKET_ETHEADER_SIZE);
+ if (uTotalPacketLength1 < VBOXNETFLT_PACKET_ETHEADER_SIZE)
+ return NULL;
+
+ NdisQueryBufferSafe(pBuffer1, &pEth, &cbLength1, NormalPagePriority);
+ Assert(cbLength1 >= VBOXNETFLT_PACKET_ETHEADER_SIZE);
+ if (cbLength1 < VBOXNETFLT_PACKET_ETHEADER_SIZE)
+ return NULL;
+
+ return pEth;
+}
+
+DECLHIDDEN(PRTNETETHERHDR) vboxNetFltWinGetEthHdrSG(PINTNETSG pSG)
+{
+ Assert(pSG->cSegsUsed);
+ Assert(pSG->cSegsAlloc >= pSG->cSegsUsed);
+ Assert(pSG->aSegs[0].cb >= VBOXNETFLT_PACKET_ETHEADER_SIZE);
+
+ if (!pSG->cSegsUsed)
+ return NULL;
+
+ if (pSG->aSegs[0].cb < VBOXNETFLT_PACKET_ETHEADER_SIZE)
+ return NULL;
+
+ return (PRTNETETHERHDR)pSG->aSegs[0].pv;
+}
+
+DECLHIDDEN(bool) vboxNetFltWinCheckMACs(PNDIS_PACKET pPacket, PRTMAC pDst, PRTMAC pSrc)
+{
+ PRTNETETHERHDR pHdr = vboxNetFltWinGetEthHdr(pPacket);
+ Assert(pHdr);
+
+ if (!pHdr)
+ return false;
+
+ if (pDst && memcmp(pDst, &pHdr->DstMac, sizeof(RTMAC)))
+ return false;
+
+ if (pSrc && memcmp(pSrc, &pHdr->SrcMac, sizeof(RTMAC)))
+ return false;
+
+ return true;
+}
+
+DECLHIDDEN(bool) vboxNetFltWinCheckMACsSG(PINTNETSG pSG, PRTMAC pDst, PRTMAC pSrc)
+{
+ PRTNETETHERHDR pHdr = vboxNetFltWinGetEthHdrSG(pSG);
+ Assert(pHdr);
+
+ if (!pHdr)
+ return false;
+
+ if (pDst && memcmp(pDst, &pHdr->DstMac, sizeof(RTMAC)))
+ return false;
+
+ if (pSrc && memcmp(pSrc, &pHdr->SrcMac, sizeof(RTMAC)))
+ return false;
+
+ return true;
+}
+#endif
+
+# if !defined(VBOX_LOOPBACK_USEFLAGS) || defined(DEBUG_NETFLT_PACKETS)
+/*
+ * answers whether the two given packets match based on the packet length and the first cbMatch bytes of the packets
+ * if cbMatch < 0 matches complete packets.
+ */
+DECLHIDDEN(bool) vboxNetFltWinMatchPackets(PNDIS_PACKET pPacket1, PNDIS_PACKET pPacket2, const INT cbMatch)
+{
+ UINT cBufCount1;
+ PNDIS_BUFFER pBuffer1;
+ UINT uTotalPacketLength1;
+ uint8_t *pbMemBuf1 = NULL;
+ UINT cbLength1 = 0;
+
+ UINT cBufCount2;
+ PNDIS_BUFFER pBuffer2;
+ UINT uTotalPacketLength2;
+ uint8_t *pbMemBuf2 = NULL;
+ UINT cbLength2 = 0;
+ bool bMatch = true;
+
+#ifdef DEBUG_NETFLT_PACKETS
+ bool bCompleteMatch = false;
+#endif
+
+ NdisQueryPacket(pPacket1, NULL, &cBufCount1, &pBuffer1, &uTotalPacketLength1);
+ NdisQueryPacket(pPacket2, NULL, &cBufCount2, &pBuffer2, &uTotalPacketLength2);
+
+ Assert(pBuffer1);
+ Assert(pBuffer2);
+
+ if (uTotalPacketLength1 != uTotalPacketLength2)
+ {
+ bMatch = false;
+ }
+ else
+ {
+ UINT ucbLength2Match = 0;
+ UINT ucbMatch;
+ if (cbMatch < 0 || (UINT)cbMatch > uTotalPacketLength1)
+ {
+ /* NOTE: assuming uTotalPacketLength1 == uTotalPacketLength2*/
+ ucbMatch = uTotalPacketLength1;
+#ifdef DEBUG_NETFLT_PACKETS
+ bCompleteMatch = true;
+#endif
+ }
+ else
+ {
+ ucbMatch = (UINT)cbMatch;
+ }
+
+ for (;;)
+ {
+ if (!cbLength1)
+ {
+ NdisQueryBufferSafe(pBuffer1, &pbMemBuf1, &cbLength1, NormalPagePriority);
+ NdisGetNextBuffer(pBuffer1, &pBuffer1);
+ }
+ else
+ {
+ Assert(pbMemBuf1);
+ Assert(ucbLength2Match);
+ pbMemBuf1 += ucbLength2Match;
+ }
+
+ if (!cbLength2)
+ {
+ NdisQueryBufferSafe(pBuffer2, &pbMemBuf2, &cbLength2, NormalPagePriority);
+ NdisGetNextBuffer(pBuffer2, &pBuffer2);
+ }
+ else
+ {
+ Assert(pbMemBuf2);
+ Assert(ucbLength2Match);
+ pbMemBuf2 += ucbLength2Match;
+ }
+
+ ucbLength2Match = MIN(ucbMatch, cbLength1);
+ ucbLength2Match = MIN(ucbLength2Match, cbLength2);
+
+ if (memcmp(pbMemBuf1, pbMemBuf2, ucbLength2Match))
+ {
+ bMatch = false;
+ break;
+ }
+
+ ucbMatch -= ucbLength2Match;
+ if (!ucbMatch)
+ break;
+
+ cbLength1 -= ucbLength2Match;
+ cbLength2 -= ucbLength2Match;
+ }
+ }
+
+#ifdef DEBUG_NETFLT_PACKETS
+ if (bMatch && !bCompleteMatch)
+ {
+ /* check that the packets fully match */
+ DBG_CHECK_PACKETS(pPacket1, pPacket2);
+ }
+#endif
+
+ return bMatch;
+}
+
+/*
+ * answers whether the ndis packet and PINTNETSG match based on the packet length and the first cbMatch bytes of the packet and PINTNETSG
+ * if cbMatch < 0 matches complete packets.
+ */
+DECLHIDDEN(bool) vboxNetFltWinMatchPacketAndSG(PNDIS_PACKET pPacket, PINTNETSG pSG, const INT cbMatch)
+{
+ UINT cBufCount1;
+ PNDIS_BUFFER pBuffer1;
+ UINT uTotalPacketLength1;
+ uint8_t *pbMemBuf1 = NULL;
+ UINT cbLength1 = 0;
+ UINT uTotalPacketLength2 = pSG->cbTotal;
+ uint8_t *pbMemBuf2 = NULL;
+ UINT cbLength2 = 0;
+ bool bMatch = true;
+ bool bCompleteMatch = false;
+ UINT i = 0;
+
+ NdisQueryPacket(pPacket, NULL, &cBufCount1, &pBuffer1, &uTotalPacketLength1);
+
+ Assert(pBuffer1);
+ Assert(pSG->cSegsUsed);
+ Assert(pSG->cSegsAlloc >= pSG->cSegsUsed);
+
+ if (uTotalPacketLength1 != uTotalPacketLength2)
+ {
+ AssertFailed();
+ bMatch = false;
+ }
+ else
+ {
+ UINT ucbLength2Match = 0;
+ UINT ucbMatch;
+
+ if (cbMatch < 0 || (UINT)cbMatch > uTotalPacketLength1)
+ {
+ /* NOTE: assuming uTotalPacketLength1 == uTotalPacketLength2*/
+ ucbMatch = uTotalPacketLength1;
+ bCompleteMatch = true;
+ }
+ else
+ {
+ ucbMatch = (UINT)cbMatch;
+ }
+
+ for (;;)
+ {
+ if (!cbLength1)
+ {
+ NdisQueryBufferSafe(pBuffer1, &pbMemBuf1, &cbLength1, NormalPagePriority);
+ NdisGetNextBuffer(pBuffer1, &pBuffer1);
+ }
+ else
+ {
+ Assert(pbMemBuf1);
+ Assert(ucbLength2Match);
+ pbMemBuf1 += ucbLength2Match;
+ }
+
+ if (!cbLength2)
+ {
+ Assert(i < pSG->cSegsUsed);
+ pbMemBuf2 = (uint8_t*)pSG->aSegs[i].pv;
+ cbLength2 = pSG->aSegs[i].cb;
+ i++;
+ }
+ else
+ {
+ Assert(pbMemBuf2);
+ Assert(ucbLength2Match);
+ pbMemBuf2 += ucbLength2Match;
+ }
+
+ ucbLength2Match = MIN(ucbMatch, cbLength1);
+ ucbLength2Match = MIN(ucbLength2Match, cbLength2);
+
+ if (memcmp(pbMemBuf1, pbMemBuf2, ucbLength2Match))
+ {
+ bMatch = false;
+ AssertFailed();
+ break;
+ }
+
+ ucbMatch -= ucbLength2Match;
+ if (!ucbMatch)
+ break;
+
+ cbLength1 -= ucbLength2Match;
+ cbLength2 -= ucbLength2Match;
+ }
+ }
+
+ if (bMatch && !bCompleteMatch)
+ {
+ /* check that the packets fully match */
+ DBG_CHECK_PACKET_AND_SG(pPacket, pSG);
+ }
+ return bMatch;
+}
+
+# if 0
+/*
+ * answers whether the two PINTNETSGs match based on the packet length and the first cbMatch bytes of the PINTNETSG
+ * if cbMatch < 0 matches complete packets.
+ */
+static bool vboxNetFltWinMatchSGs(PINTNETSG pSG1, PINTNETSG pSG2, const INT cbMatch)
+{
+ UINT uTotalPacketLength1 = pSG1->cbTotal;
+ PVOID pbMemBuf1 = NULL;
+ UINT cbLength1 = 0;
+ UINT i1 = 0;
+ UINT uTotalPacketLength2 = pSG2->cbTotal;
+ PVOID pbMemBuf2 = NULL;
+ UINT cbLength2 = 0;
+
+ bool bMatch = true;
+ bool bCompleteMatch = false;
+ UINT i2 = 0;
+
+ Assert(pSG1->cSegsUsed);
+ Assert(pSG2->cSegsUsed);
+ Assert(pSG1->cSegsAlloc >= pSG1->cSegsUsed);
+ Assert(pSG2->cSegsAlloc >= pSG2->cSegsUsed);
+
+ if (uTotalPacketLength1 != uTotalPacketLength2)
+ {
+ AssertFailed();
+ bMatch = false;
+ }
+ else
+ {
+ UINT ucbMatch;
+ if (cbMatch < 0 || (UINT)cbMatch > uTotalPacketLength1)
+ {
+ /* NOTE: assuming uTotalPacketLength1 == uTotalPacketLength2*/
+ ucbMatch = uTotalPacketLength1;
+ bCompleteMatch = true;
+ }
+ else
+ {
+ ucbMatch = (UINT)cbMatch;
+ }
+
+ do
+ {
+ UINT ucbLength2Match;
+ if (!cbLength1)
+ {
+ Assert(i1 < pSG1->cSegsUsed);
+ pbMemBuf1 = pSG1->aSegs[i1].pv;
+ cbLength1 = pSG1->aSegs[i1].cb;
+ i1++;
+ }
+
+ if (!cbLength2)
+ {
+ Assert(i2 < pSG2->cSegsUsed);
+ pbMemBuf2 = pSG2->aSegs[i2].pv;
+ cbLength2 = pSG2->aSegs[i2].cb;
+ i2++;
+ }
+
+ ucbLength2Match = MIN(ucbMatch, cbLength1);
+ ucbLength2Match = MIN(ucbLength2Match, cbLength2);
+
+ if (memcmp(pbMemBuf1, pbMemBuf2, ucbLength2Match))
+ {
+ bMatch = false;
+ AssertFailed();
+ break;
+ }
+ ucbMatch -= ucbLength2Match;
+ cbLength1 -= ucbLength2Match;
+ cbLength2 -= ucbLength2Match;
+ } while (ucbMatch);
+ }
+
+ if (bMatch && !bCompleteMatch)
+ {
+ /* check that the packets fully match */
+ DBG_CHECK_SGS(pSG1, pSG2);
+ }
+ return bMatch;
+}
+# endif
+# endif
+#endif
+
+static void vboxNetFltWinFiniNetFltBase()
+{
+ do
+ {
+ vboxNetFltDeleteGlobals(&g_VBoxNetFltGlobals);
+
+ /*
+ * Undo the work done during start (in reverse order).
+ */
+ memset(&g_VBoxNetFltGlobals, 0, sizeof(g_VBoxNetFltGlobals));
+
+ RTLogDestroy(RTLogRelSetDefaultInstance(NULL));
+ RTLogDestroy(RTLogSetDefaultInstance(NULL));
+
+ RTR0Term();
+ } while (0);
+}
+
+/*
+ * Defines max timeout for waiting for driver unloading
+ * (3000 * 100 ms = 5 minutes)
+ */
+#define MAX_UNLOAD_PROBES 3000
+
+static int vboxNetFltWinFiniIdc()
+{
+ int rc;
+ int i;
+
+ vboxNetFltWinStopInitIdcProbing();
+
+ if (g_bVBoxIdcInitialized)
+ {
+ for (i = 0; (rc = vboxNetFltTryDeleteIdc(&g_VBoxNetFltGlobals)) == VERR_WRONG_ORDER
+ && i < MAX_UNLOAD_PROBES; i++)
+ {
+ RTThreadSleep(100);
+ }
+ if (i == MAX_UNLOAD_PROBES)
+ {
+ // seems something hungs in driver
+ LogFlow(("vboxNetFltWinFiniIdc - Can't delete Idc. pInH=%p cFRefs=%d fIDcOpen=%s",
+ g_VBoxNetFltGlobals.pInstanceHead, g_VBoxNetFltGlobals.cFactoryRefs,
+ g_VBoxNetFltGlobals.fIDCOpen ? "true" : "false"));
+ LogFlow(("vboxNetFltWinFiniIdc g_VBoxNetFltGlobalsWin cDvRefs=%d hDev=%x pDev=%p Mp=%x \n",
+ g_VBoxNetFltGlobalsWin.cDeviceRefs, g_VBoxNetFltGlobalsWin.hDevice,
+ g_VBoxNetFltGlobalsWin.pDevObj, g_VBoxNetFltGlobalsWin.Mp.hMiniport));
+ Assert(i == MAX_UNLOAD_PROBES);
+ return VERR_WRONG_ORDER;
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ g_bVBoxIdcInitialized = false;
+ }
+ }
+ else
+ {
+ rc = VINF_SUCCESS;
+ }
+ return rc;
+
+}
+
+static int vboxNetFltWinFiniNetFlt()
+{
+ int rc = vboxNetFltWinFiniIdc();
+ if (RT_SUCCESS(rc))
+ {
+ vboxNetFltWinFiniNetFltBase();
+ }
+ return rc;
+}
+
+/**
+ * base netflt initialization
+ */
+static int vboxNetFltWinInitNetFltBase()
+{
+ int rc;
+
+ do
+ {
+ Assert(!g_bVBoxIdcInitialized);
+
+ rc = RTR0Init(0);
+ if (!RT_SUCCESS(rc))
+ {
+ break;
+ }
+
+ memset(&g_VBoxNetFltGlobals, 0, sizeof(g_VBoxNetFltGlobals));
+ rc = vboxNetFltInitGlobals(&g_VBoxNetFltGlobals);
+ if (!RT_SUCCESS(rc))
+ {
+ RTR0Term();
+ break;
+ }
+ }while (0);
+
+ return rc;
+}
+
+/**
+ * initialize IDC
+ */
+static int vboxNetFltWinInitIdc()
+{
+ int rc;
+
+ do
+ {
+ if (g_bVBoxIdcInitialized)
+ {
+ rc = VINF_ALREADY_INITIALIZED;
+ break;
+ }
+
+ /*
+ * connect to the support driver.
+ *
+ * This will call back vboxNetFltOsOpenSupDrv (and maybe vboxNetFltOsCloseSupDrv)
+ * for establishing the connect to the support driver.
+ */
+ rc = vboxNetFltInitIdc(&g_VBoxNetFltGlobals);
+ if (!RT_SUCCESS(rc))
+ {
+ break;
+ }
+
+ g_bVBoxIdcInitialized = true;
+ } while (0);
+
+ return rc;
+}
+
+static VOID vboxNetFltWinInitIdcProbingWorker(PVOID pvContext)
+{
+ PINIT_IDC_INFO pInitIdcInfo = (PINIT_IDC_INFO)pvContext;
+ int rc = vboxNetFltWinInitIdc();
+ if (RT_FAILURE(rc))
+ {
+ bool bInterupted = ASMAtomicUoReadBool(&pInitIdcInfo->bStop);
+ if (!bInterupted)
+ {
+ RTThreadSleep(1000); /* 1 s */
+ bInterupted = ASMAtomicUoReadBool(&pInitIdcInfo->bStop);
+ if (!bInterupted)
+ {
+ vboxNetFltWinJobEnqueueJob(&g_VBoxJobQueue, &pInitIdcInfo->Job, false);
+ return;
+ }
+ }
+
+ /* it's interrupted */
+ rc = VERR_INTERRUPTED;
+ }
+
+ ASMAtomicUoWriteS32(&pInitIdcInfo->rc, rc);
+ KeSetEvent(&pInitIdcInfo->hCompletionEvent, 0, FALSE);
+}
+
+static int vboxNetFltWinStopInitIdcProbing()
+{
+ if (!g_VBoxInitIdcInfo.bInitialized)
+ return VERR_INVALID_STATE;
+
+ ASMAtomicUoWriteBool(&g_VBoxInitIdcInfo.bStop, true);
+ KeWaitForSingleObject(&g_VBoxInitIdcInfo.hCompletionEvent, Executive, KernelMode, FALSE, NULL);
+
+ return g_VBoxInitIdcInfo.rc;
+}
+
+static int vboxNetFltWinStartInitIdcProbing()
+{
+ Assert(!g_bVBoxIdcInitialized);
+ KeInitializeEvent(&g_VBoxInitIdcInfo.hCompletionEvent, NotificationEvent, FALSE);
+ g_VBoxInitIdcInfo.bStop = false;
+ g_VBoxInitIdcInfo.bInitialized = true;
+ vboxNetFltWinJobInit(&g_VBoxInitIdcInfo.Job, vboxNetFltWinInitIdcProbingWorker, &g_VBoxInitIdcInfo, false);
+ vboxNetFltWinJobEnqueueJob(&g_VBoxJobQueue, &g_VBoxInitIdcInfo.Job, false);
+ return VINF_SUCCESS;
+}
+
+static int vboxNetFltWinInitNetFlt()
+{
+ int rc;
+
+ do
+ {
+ rc = vboxNetFltWinInitNetFltBase();
+ if (RT_FAILURE(rc))
+ {
+ AssertFailed();
+ break;
+ }
+
+ /*
+ * connect to the support driver.
+ *
+ * This will call back vboxNetFltOsOpenSupDrv (and maybe vboxNetFltOsCloseSupDrv)
+ * for establishing the connect to the support driver.
+ */
+ rc = vboxNetFltWinInitIdc();
+ if (RT_FAILURE(rc))
+ {
+ AssertFailed();
+ vboxNetFltWinFiniNetFltBase();
+ break;
+ }
+ } while (0);
+
+ return rc;
+}
+
+/* detach*/
+static int vboxNetFltWinDeleteInstance(PVBOXNETFLTINS pThis)
+{
+ LogFlow(("vboxNetFltWinDeleteInstance: pThis=0x%p \n", pThis));
+
+ Assert(KeGetCurrentIrql() < DISPATCH_LEVEL);
+ Assert(pThis);
+ Assert(pThis->fDisconnectedFromHost);
+ Assert(!pThis->fRediscoveryPending);
+ Assert(pThis->enmTrunkState != INTNETTRUNKIFSTATE_ACTIVE);
+#ifndef VBOXNETADP
+ Assert(pThis->u.s.WinIf.PtState.OpState == kVBoxNetDevOpState_Deinitialized);
+ Assert(!pThis->u.s.WinIf.hBinding);
+#endif
+ Assert(pThis->u.s.WinIf.MpState.OpState == kVBoxNetDevOpState_Deinitialized);
+#ifndef VBOXNETFLT_NO_PACKET_QUEUE
+ Assert(!pThis->u.s.PacketQueueWorker.pSG);
+#endif
+
+ RTSemMutexDestroy(pThis->u.s.hWinIfMutex);
+
+ vboxNetFltWinDrvDereference();
+
+ return VINF_SUCCESS;
+}
+
+static NDIS_STATUS vboxNetFltWinDisconnectIt(PVBOXNETFLTINS pInstance)
+{
+#ifndef VBOXNETFLT_NO_PACKET_QUEUE
+ vboxNetFltWinQuFiniPacketQueue(pInstance);
+#else
+ RT_NOREF1(pInstance);
+#endif
+ return NDIS_STATUS_SUCCESS;
+}
+
+/* detach*/
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinDetachFromInterface(PVBOXNETFLTINS pNetFlt, bool bOnUnbind)
+{
+ NDIS_STATUS Status;
+ int rc;
+ LogFlowFunc(("ENTER: pThis=%0xp\n", pNetFlt));
+
+ Assert(KeGetCurrentIrql() < DISPATCH_LEVEL);
+ Assert(pNetFlt);
+
+ /* paranoia to ensure the instance is not removed while we're waiting on the mutex
+ * in case ndis does something unpredictable, e.g. calls our miniport halt independently
+ * from protocol unbind and concurrently with it*/
+ vboxNetFltRetain(pNetFlt, false);
+
+ rc = RTSemMutexRequest(pNetFlt->u.s.hWinIfMutex, RT_INDEFINITE_WAIT);
+ if (RT_SUCCESS(rc))
+ {
+ Assert(vboxNetFltWinGetWinIfState(pNetFlt) == kVBoxWinIfState_Connected);
+ Assert(vboxNetFltWinGetOpState(&pNetFlt->u.s.WinIf.MpState) == kVBoxNetDevOpState_Initialized);
+#ifndef VBOXNETADP
+ Assert(vboxNetFltWinGetOpState(&pNetFlt->u.s.WinIf.PtState) == kVBoxNetDevOpState_Initialized);
+#endif
+ if (vboxNetFltWinGetWinIfState(pNetFlt) == kVBoxWinIfState_Connected)
+ {
+ vboxNetFltWinSetWinIfState(pNetFlt, kVBoxWinIfState_Disconnecting);
+#ifndef VBOXNETADP
+ Status = vboxNetFltWinPtDoUnbinding(pNetFlt, bOnUnbind);
+#else
+ Status = vboxNetFltWinMpDoDeinitialization(pNetFlt);
+#endif
+ Assert(Status == NDIS_STATUS_SUCCESS);
+
+ vboxNetFltWinSetWinIfState(pNetFlt, kVBoxWinIfState_Disconnected);
+ Assert(vboxNetFltWinGetOpState(&pNetFlt->u.s.WinIf.MpState) == kVBoxNetDevOpState_Deinitialized);
+#ifndef VBOXNETADP
+ Assert(vboxNetFltWinGetOpState(&pNetFlt->u.s.WinIf.PtState) == kVBoxNetDevOpState_Deinitialized);
+#endif
+ vboxNetFltWinPtFiniWinIf(&pNetFlt->u.s.WinIf);
+
+ /* we're unbinding, make an unbind-related release */
+ vboxNetFltRelease(pNetFlt, false);
+ }
+ else
+ {
+ AssertBreakpoint();
+#ifndef VBOXNETADP
+ pNetFlt->u.s.WinIf.OpenCloseStatus = NDIS_STATUS_FAILURE;
+#endif
+ if (!bOnUnbind)
+ {
+ vboxNetFltWinSetOpState(&pNetFlt->u.s.WinIf.MpState, kVBoxNetDevOpState_Deinitialized);
+ }
+ Status = NDIS_STATUS_FAILURE;
+ }
+ RTSemMutexRelease(pNetFlt->u.s.hWinIfMutex);
+ }
+ else
+ {
+ AssertBreakpoint();
+ Status = NDIS_STATUS_FAILURE;
+ }
+
+ /* release for the retain we made before waining on the mutex */
+ vboxNetFltRelease(pNetFlt, false);
+
+ LogFlowFunc(("LEAVE: Status 0x%x\n", Status));
+
+ return Status;
+}
+
+
+/**
+ * Checks if the host (not us) has put the adapter in promiscuous mode.
+ *
+ * @returns true if promiscuous, false if not.
+ * @param pThis The instance.
+ */
+static bool vboxNetFltWinIsPromiscuous2(PVBOXNETFLTINS pThis)
+{
+#ifndef VBOXNETADP
+ if (VBOXNETFLT_PROMISCUOUS_SUPPORTED(pThis))
+ {
+ bool bPromiscuous;
+ if (!vboxNetFltWinReferenceWinIf(pThis))
+ return false;
+
+ bPromiscuous = (pThis->u.s.WinIf.fUpperProtocolSetFilter & NDIS_PACKET_TYPE_PROMISCUOUS) == NDIS_PACKET_TYPE_PROMISCUOUS;
+ /*vboxNetFltWinIsPromiscuous(pAdapt);*/
+
+ vboxNetFltWinDereferenceWinIf(pThis);
+ return bPromiscuous;
+ }
+ return false;
+#else
+ RT_NOREF1(pThis);
+ return true;
+#endif
+}
+
+
+/**
+ * Report the MAC address, promiscuous mode setting, GSO capabilities and
+ * no-preempt destinations to the internal network.
+ *
+ * Does nothing if we're not currently connected to an internal network.
+ *
+ * @param pThis The instance data.
+ */
+static void vboxNetFltWinReportStuff(PVBOXNETFLTINS pThis)
+{
+ /** @todo Keep these up to date, esp. the promiscuous mode bit. */
+ if (pThis->pSwitchPort
+ && vboxNetFltTryRetainBusyNotDisconnected(pThis))
+ {
+ pThis->pSwitchPort->pfnReportMacAddress(pThis->pSwitchPort, &pThis->u.s.MacAddr);
+ pThis->pSwitchPort->pfnReportPromiscuousMode(pThis->pSwitchPort,
+ vboxNetFltWinIsPromiscuous2(pThis));
+ pThis->pSwitchPort->pfnReportGsoCapabilities(pThis->pSwitchPort, 0,
+ INTNETTRUNKDIR_WIRE | INTNETTRUNKDIR_HOST);
+ /** @todo We should be able to do pfnXmit at DISPATCH_LEVEL... */
+ pThis->pSwitchPort->pfnReportNoPreemptDsts(pThis->pSwitchPort, 0 /* none */);
+ vboxNetFltRelease(pThis, true /*fBusy*/);
+ }
+}
+
+/**
+ * Worker for vboxNetFltWinAttachToInterface.
+ *
+ * @param pAttachInfo Structure for communicating with
+ * vboxNetFltWinAttachToInterface.
+ */
+static void vboxNetFltWinAttachToInterfaceWorker(PATTACH_INFO pAttachInfo)
+{
+ PVBOXNETFLTINS pThis = pAttachInfo->pNetFltIf;
+ NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
+ int rc;
+
+ Assert(KeGetCurrentIrql() == PASSIVE_LEVEL);
+
+ /* to ensure we're not removed while we're here */
+ vboxNetFltRetain(pThis, false);
+
+ rc = RTSemMutexRequest(pThis->u.s.hWinIfMutex, RT_INDEFINITE_WAIT);
+ if (RT_SUCCESS(rc))
+ {
+ Assert(vboxNetFltWinGetWinIfState(pThis) == kVBoxWinIfState_Disconnected);
+ Assert(vboxNetFltWinGetOpState(&pThis->u.s.WinIf.MpState) == kVBoxNetDevOpState_Deinitialized);
+#ifndef VBOXNETADP
+ Assert(vboxNetFltWinGetOpState(&pThis->u.s.WinIf.PtState) == kVBoxNetDevOpState_Deinitialized);
+#endif
+ if (vboxNetFltWinGetWinIfState(pThis) == kVBoxWinIfState_Disconnected)
+ {
+ if (pAttachInfo->fRediscovery)
+ {
+ /* rediscovery means adaptor bind is performed while intnet is already using it
+ * i.e. adaptor was unbound while being used by intnet and now being bound back again */
+ Assert( ((VBOXNETFTLINSSTATE)ASMAtomicUoReadU32((uint32_t volatile *)&pThis->enmState))
+ == kVBoxNetFltInsState_Connected);
+ }
+#ifndef VBOXNETADP
+ Status = vboxNetFltWinPtInitWinIf(&pThis->u.s.WinIf, pAttachInfo->pCreateContext->pOurName);
+#else
+ Status = vboxNetFltWinPtInitWinIf(&pThis->u.s.WinIf);
+#endif
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ vboxNetFltWinSetWinIfState(pThis, kVBoxWinIfState_Connecting);
+
+#ifndef VBOXNETADP
+ Status = vboxNetFltWinPtDoBinding(pThis, pAttachInfo->pCreateContext->pOurName, pAttachInfo->pCreateContext->pBindToName);
+#else
+ Status = vboxNetFltWinMpDoInitialization(pThis, pAttachInfo->pCreateContext->hMiniportAdapter, pAttachInfo->pCreateContext->hWrapperConfigurationContext);
+#endif
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ if (!pAttachInfo->fRediscovery)
+ vboxNetFltWinDrvReference();
+#ifndef VBOXNETADP
+ if (pThis->u.s.WinIf.OpenCloseStatus == NDIS_STATUS_SUCCESS)
+#endif
+ {
+ vboxNetFltWinSetWinIfState(pThis, kVBoxWinIfState_Connected);
+#ifndef VBOXNETADP
+ Assert(vboxNetFltWinGetOpState(&pThis->u.s.WinIf.PtState) == kVBoxNetDevOpState_Initialized);
+#endif
+ /* 4. mark as connected */
+ RTSpinlockAcquire(pThis->hSpinlock);
+ ASMAtomicUoWriteBool(&pThis->fDisconnectedFromHost, false);
+ RTSpinlockRelease(pThis->hSpinlock);
+
+ pAttachInfo->Status = VINF_SUCCESS;
+ pAttachInfo->pCreateContext->Status = NDIS_STATUS_SUCCESS;
+
+ RTSemMutexRelease(pThis->u.s.hWinIfMutex);
+
+ vboxNetFltRelease(pThis, false);
+
+ /* 5. Report MAC address, promiscuousness and GSO capabilities. */
+ vboxNetFltWinReportStuff(pThis);
+
+ return;
+ }
+#ifndef VBOXNETADP /* unreachable for VBOXNETADP because of the return above */
+ AssertBreakpoint();
+
+ if (!pAttachInfo->fRediscovery)
+ {
+ vboxNetFltWinDrvDereference();
+ }
+# ifndef VBOXNETADP
+ vboxNetFltWinPtDoUnbinding(pThis, true);
+/*# else - unreachable
+ vboxNetFltWinMpDoDeinitialization(pThis); */
+# endif
+#endif
+ }
+ AssertBreakpoint();
+ vboxNetFltWinPtFiniWinIf(&pThis->u.s.WinIf);
+ }
+ AssertBreakpoint();
+ vboxNetFltWinSetWinIfState(pThis, kVBoxWinIfState_Disconnected);
+ Assert(vboxNetFltWinGetOpState(&pThis->u.s.WinIf.MpState) == kVBoxNetDevOpState_Deinitialized);
+#ifndef VBOXNETADP
+ Assert(vboxNetFltWinGetOpState(&pThis->u.s.WinIf.PtState) == kVBoxNetDevOpState_Deinitialized);
+#endif
+ }
+ AssertBreakpoint();
+
+ pAttachInfo->Status = VERR_GENERAL_FAILURE;
+ pAttachInfo->pCreateContext->Status = Status;
+ RTSemMutexRelease(pThis->u.s.hWinIfMutex);
+ }
+ else
+ {
+ AssertBreakpoint();
+ pAttachInfo->Status = rc;
+ }
+
+ vboxNetFltRelease(pThis, false);
+
+ return;
+}
+
+/**
+ * Common code for vboxNetFltOsInitInstance and
+ * vboxNetFltOsMaybeRediscovered.
+ *
+ * @returns IPRT status code.
+ * @param pThis The instance.
+ * @param fRediscovery True if vboxNetFltOsMaybeRediscovered is calling,
+ * false if it's vboxNetFltOsInitInstance.
+ */
+static int vboxNetFltWinAttachToInterface(PVBOXNETFLTINS pThis, void * pContext, bool fRediscovery)
+{
+ ATTACH_INFO Info;
+ Info.pNetFltIf = pThis;
+ Info.fRediscovery = fRediscovery;
+ Info.pCreateContext = (PCREATE_INSTANCE_CONTEXT)pContext;
+
+ vboxNetFltWinAttachToInterfaceWorker(&Info);
+
+ return Info.Status;
+}
+static NTSTATUS vboxNetFltWinPtDevDispatch(IN PDEVICE_OBJECT pDevObj, IN PIRP pIrp)
+{
+ RT_NOREF1(pDevObj);
+ PIO_STACK_LOCATION pIrpSl = IoGetCurrentIrpStackLocation(pIrp);;
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ switch (pIrpSl->MajorFunction)
+ {
+ case IRP_MJ_DEVICE_CONTROL:
+ Status = STATUS_NOT_SUPPORTED;
+ break;
+ case IRP_MJ_CREATE:
+ case IRP_MJ_CLEANUP:
+ case IRP_MJ_CLOSE:
+ break;
+ default:
+ AssertFailed();
+ break;
+ }
+
+ pIrp->IoStatus.Status = Status;
+ IoCompleteRequest(pIrp, IO_NO_INCREMENT);
+
+ return Status;
+}
+
+static NDIS_STATUS vboxNetFltWinDevCreate(PVBOXNETFLTGLOBALS_WIN pGlobals)
+{
+ NDIS_STRING DevName, LinkName;
+ PDRIVER_DISPATCH aMajorFunctions[IRP_MJ_MAXIMUM_FUNCTION+1];
+ NdisInitUnicodeString(&DevName, VBOXNETFLT_NAME_DEVICE);
+ NdisInitUnicodeString(&LinkName, VBOXNETFLT_NAME_LINK);
+
+ Assert(!pGlobals->hDevice);
+ Assert(!pGlobals->pDevObj);
+ NdisZeroMemory(aMajorFunctions, sizeof (aMajorFunctions));
+ aMajorFunctions[IRP_MJ_CREATE] = vboxNetFltWinPtDevDispatch;
+ aMajorFunctions[IRP_MJ_CLEANUP] = vboxNetFltWinPtDevDispatch;
+ aMajorFunctions[IRP_MJ_CLOSE] = vboxNetFltWinPtDevDispatch;
+ aMajorFunctions[IRP_MJ_DEVICE_CONTROL] = vboxNetFltWinPtDevDispatch;
+
+ NDIS_STATUS Status = NdisMRegisterDevice(pGlobals->Mp.hNdisWrapper,
+ &DevName, &LinkName,
+ aMajorFunctions,
+ &pGlobals->pDevObj,
+ &pGlobals->hDevice);
+ Assert(Status == NDIS_STATUS_SUCCESS);
+ return Status;
+}
+
+static NDIS_STATUS vboxNetFltWinDevDestroy(PVBOXNETFLTGLOBALS_WIN pGlobals)
+{
+ Assert(pGlobals->hDevice);
+ Assert(pGlobals->pDevObj);
+ NDIS_STATUS Status = NdisMDeregisterDevice(pGlobals->hDevice);
+ Assert(Status == NDIS_STATUS_SUCCESS);
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ pGlobals->hDevice = NULL;
+ pGlobals->pDevObj = NULL;
+ }
+ return Status;
+}
+
+static NDIS_STATUS vboxNetFltWinDevCreateReference(PVBOXNETFLTGLOBALS_WIN pGlobals)
+{
+ Assert(KeGetCurrentIrql() == PASSIVE_LEVEL);
+ NDIS_STATUS Status = KeWaitForSingleObject(&pGlobals->SynchEvent, Executive, KernelMode, FALSE, NULL);
+ Assert(Status == STATUS_SUCCESS);
+ if (Status == STATUS_SUCCESS)
+ {
+ Assert(pGlobals->cDeviceRefs >= 0);
+ if (++pGlobals->cDeviceRefs == 1)
+ {
+ Status = vboxNetFltWinDevCreate(pGlobals);
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ ObReferenceObject(pGlobals->pDevObj);
+ }
+ }
+ else
+ {
+ Status = NDIS_STATUS_SUCCESS;
+ }
+ KeSetEvent(&pGlobals->SynchEvent, 0, FALSE);
+ }
+ else
+ {
+ /* should never happen actually */
+ AssertFailed();
+ Status = NDIS_STATUS_FAILURE;
+ }
+ return Status;
+}
+
+static NDIS_STATUS vboxNetFltWinDevDereference(PVBOXNETFLTGLOBALS_WIN pGlobals)
+{
+ Assert(KeGetCurrentIrql() == PASSIVE_LEVEL);
+ NDIS_STATUS Status = KeWaitForSingleObject(&pGlobals->SynchEvent, Executive, KernelMode, FALSE, NULL);
+ Assert(Status == STATUS_SUCCESS);
+ if (Status == STATUS_SUCCESS)
+ {
+ Assert(pGlobals->cDeviceRefs > 0);
+ if (!(--pGlobals->cDeviceRefs))
+ {
+ ObDereferenceObject(pGlobals->pDevObj);
+ Status = vboxNetFltWinDevDestroy(pGlobals);
+ }
+ else
+ {
+ Status = NDIS_STATUS_SUCCESS;
+ }
+ KeSetEvent(&pGlobals->SynchEvent, 0, FALSE);
+ }
+ else
+ {
+ /* should never happen actually */
+ AssertFailed();
+ Status = NDIS_STATUS_FAILURE;
+ }
+ return Status;
+}
+
+/* reference the driver module to prevent driver unload */
+DECLHIDDEN(void) vboxNetFltWinDrvReference()
+{
+ vboxNetFltWinDevCreateReference(&g_VBoxNetFltGlobalsWin);
+}
+
+/* dereference the driver module to prevent driver unload */
+DECLHIDDEN(void) vboxNetFltWinDrvDereference()
+{
+ vboxNetFltWinDevDereference(&g_VBoxNetFltGlobalsWin);
+}
+
+/*
+ *
+ * The OS specific interface definition
+ *
+ */
+
+
+bool vboxNetFltOsMaybeRediscovered(PVBOXNETFLTINS pThis)
+{
+ /* AttachToInterface true if disconnected */
+ return !ASMAtomicUoReadBool(&pThis->fDisconnectedFromHost);
+}
+
+int vboxNetFltPortOsXmit(PVBOXNETFLTINS pThis, void *pvIfData, PINTNETSG pSG, uint32_t fDst)
+{
+ RT_NOREF1(pvIfData);
+ int rc = VINF_SUCCESS;
+ uint32_t cRefs = 0;
+#ifndef VBOXNETADP
+ if (fDst & INTNETTRUNKDIR_WIRE)
+ cRefs++;
+ if (fDst & INTNETTRUNKDIR_HOST)
+ cRefs++;
+#else
+ if ((fDst & INTNETTRUNKDIR_WIRE) || (fDst & INTNETTRUNKDIR_HOST))
+ cRefs = 1;
+#endif
+
+ AssertReturn(cRefs, VINF_SUCCESS);
+
+ if (!vboxNetFltWinIncReferenceWinIf(pThis, cRefs))
+ {
+ return VERR_GENERAL_FAILURE;
+ }
+#ifndef VBOXNETADP
+ if (fDst & INTNETTRUNKDIR_WIRE)
+ {
+ PNDIS_PACKET pPacket;
+
+ pPacket = vboxNetFltWinNdisPacketFromSG(pThis, pSG, NULL /*pBufToFree*/,
+ true /*fToWire*/, true /*fCopyMemory*/);
+
+ if (pPacket)
+ {
+ NDIS_STATUS fStatus;
+
+#ifndef VBOX_LOOPBACK_USEFLAGS
+ /* force "don't loopback" flags to prevent loopback branch invocation in any case
+ * to avoid ndis misbehave */
+ NdisGetPacketFlags(pPacket) |= g_VBoxNetFltGlobalsWin.fPacketDontLoopBack;
+#else
+ /* this is done by default in vboxNetFltWinNdisPacketFromSG */
+#endif
+
+#if defined(DEBUG_NETFLT_PACKETS) || !defined(VBOX_LOOPBACK_USEFLAGS)
+ vboxNetFltWinLbPutSendPacket(pThis, pPacket, true /* bFromIntNet */);
+#endif
+ NdisSend(&fStatus, pThis->u.s.WinIf.hBinding, pPacket);
+ if (fStatus != NDIS_STATUS_PENDING)
+ {
+#if defined(DEBUG_NETFLT_PACKETS) || !defined(VBOX_LOOPBACK_USEFLAGS)
+ /* the status is NOT pending, complete the packet */
+ bool fTmp = vboxNetFltWinLbRemoveSendPacket(pThis, pPacket);
+ Assert(fTmp); NOREF(fTmp);
+#endif
+ if (!NT_SUCCESS(fStatus))
+ rc = VERR_GENERAL_FAILURE; /** @todo convert status to VERR_xxx */
+
+ vboxNetFltWinFreeSGNdisPacket(pPacket, true);
+ }
+ else
+ {
+ /* pending, dereference on packet complete */
+ cRefs--;
+ }
+ }
+ else
+ {
+ AssertFailed();
+ rc = VERR_NO_MEMORY;
+ }
+ }
+#endif
+
+#ifndef VBOXNETADP
+ if (fDst & INTNETTRUNKDIR_HOST)
+#else
+ if (cRefs)
+#endif
+ {
+ PNDIS_PACKET pPacket = vboxNetFltWinNdisPacketFromSG(pThis, pSG, NULL /*pBufToFree*/,
+ false /*fToWire*/, true /*fCopyMemory*/);
+ if (pPacket)
+ {
+ NdisMIndicateReceivePacket(pThis->u.s.WinIf.hMiniport, &pPacket, 1);
+ cRefs--;
+#ifdef VBOXNETADP
+ STATISTIC_INCREASE(pThis->u.s.WinIf.cRxSuccess);
+#endif
+ }
+ else
+ {
+ AssertFailed();
+#ifdef VBOXNETADP
+ STATISTIC_INCREASE(pThis->u.s.WinIf.cRxError);
+#endif
+ rc = VERR_NO_MEMORY;
+ }
+ }
+
+ Assert(cRefs <= 2);
+
+ if (cRefs)
+ {
+ vboxNetFltWinDecReferenceWinIf(pThis, cRefs);
+ }
+
+ return rc;
+}
+
+void vboxNetFltPortOsSetActive(PVBOXNETFLTINS pThis, bool fActive)
+{
+#ifndef VBOXNETADP
+ NDIS_STATUS Status;
+#endif
+ /* we first wait for all pending ops to complete
+ * this might include all packets queued for processing */
+ for (;;)
+ {
+ if (fActive)
+ {
+ if (!pThis->u.s.cModePassThruRefs)
+ {
+ break;
+ }
+ }
+ else
+ {
+ if (!pThis->u.s.cModeNetFltRefs)
+ {
+ break;
+ }
+ }
+ vboxNetFltWinSleep(2);
+ }
+
+ if (!vboxNetFltWinReferenceWinIf(pThis))
+ return;
+#ifndef VBOXNETADP
+
+ if (fActive)
+ {
+#ifdef DEBUG_misha
+ NDIS_PHYSICAL_MEDIUM PhMedium;
+ bool bPromiscSupported;
+
+ Status = vboxNetFltWinQueryPhysicalMedium(pThis, &PhMedium);
+ if (Status != NDIS_STATUS_SUCCESS)
+ {
+
+ LogRel(("vboxNetFltWinQueryPhysicalMedium failed, Status (0x%x), setting medium to NdisPhysicalMediumUnspecified\n", Status));
+ Assert(Status == NDIS_STATUS_NOT_SUPPORTED);
+ if (Status != NDIS_STATUS_NOT_SUPPORTED)
+ {
+ LogRel(("vboxNetFltWinQueryPhysicalMedium failed, Status (0x%x), setting medium to NdisPhysicalMediumUnspecified\n", Status));
+ }
+ PhMedium = NdisPhysicalMediumUnspecified;
+ }
+ else
+ {
+ LogRel(("(SUCCESS) vboxNetFltWinQueryPhysicalMedium SUCCESS\n"));
+ }
+
+ bPromiscSupported = (!(PhMedium == NdisPhysicalMediumWirelessWan
+ || PhMedium == NdisPhysicalMediumWirelessLan
+ || PhMedium == NdisPhysicalMediumNative802_11
+ || PhMedium == NdisPhysicalMediumBluetooth
+ /*|| PhMedium == NdisPhysicalMediumWiMax */
+ ));
+
+ Assert(bPromiscSupported == VBOXNETFLT_PROMISCUOUS_SUPPORTED(pThis));
+#endif
+ }
+
+ if (VBOXNETFLT_PROMISCUOUS_SUPPORTED(pThis))
+ {
+ Status = vboxNetFltWinSetPromiscuous(pThis, fActive);
+ if (Status != NDIS_STATUS_SUCCESS)
+ {
+ LogRel(("vboxNetFltWinSetPromiscuous failed, Status (0x%x), fActive (%d)\n", Status, fActive));
+ AssertFailed();
+ }
+ }
+#else
+# ifdef VBOXNETADP_REPORT_DISCONNECTED
+ if (fActive)
+ {
+ NdisMIndicateStatus(pThis->u.s.WinIf.hMiniport,
+ NDIS_STATUS_MEDIA_CONNECT,
+ (PVOID)NULL,
+ 0);
+ }
+ else
+ {
+ NdisMIndicateStatus(pThis->u.s.WinIf.hMiniport,
+ NDIS_STATUS_MEDIA_DISCONNECT,
+ (PVOID)NULL,
+ 0);
+ }
+#else
+ if (fActive)
+ {
+ /* indicate status change to make the ip settings be re-picked for dhcp */
+ NdisMIndicateStatus(pThis->u.s.WinIf.hMiniport,
+ NDIS_STATUS_MEDIA_DISCONNECT,
+ (PVOID)NULL,
+ 0);
+
+ NdisMIndicateStatus(pThis->u.s.WinIf.hMiniport,
+ NDIS_STATUS_MEDIA_CONNECT,
+ (PVOID)NULL,
+ 0);
+ }
+# endif
+#endif
+ vboxNetFltWinDereferenceWinIf(pThis);
+
+ return;
+}
+
+#ifndef VBOXNETADP
+
+DECLINLINE(bool) vboxNetFltWinIsAddrLinkLocal4(PCRTNETADDRIPV4 pAddr)
+{
+ return (pAddr->s.Lo == 0xfea9); /* 169.254 */
+}
+
+DECLINLINE(bool) vboxNetFltWinIsAddrLinkLocal6(PCRTNETADDRIPV6 pAddr)
+{
+ return ((pAddr->au8[0] == 0xfe) && ((pAddr->au8[1] & 0xc0) == 0x80));
+}
+
+void vboxNetFltWinNotifyHostAddress(PTA_ADDRESS pAddress, bool fAdded)
+{
+ void *pvAddr = NULL;
+ INTNETADDRTYPE enmAddrType = kIntNetAddrType_Invalid;
+
+ LogFlow(("==>vboxNetFltWinNotifyHostAddress: AddrType=%d %s\n",
+ pAddress->AddressType, fAdded ? "added" : "deleted"));
+ if (pAddress->AddressType == TDI_ADDRESS_TYPE_IP)
+ {
+ PTDI_ADDRESS_IP pTdiAddrIp = (PTDI_ADDRESS_IP)pAddress->Address;
+ /*
+ * Note that we do not get loopback addresses here. If we did we should
+ * have checked and ignored them too.
+ */
+ if (!vboxNetFltWinIsAddrLinkLocal4((PCRTNETADDRIPV4)(&pTdiAddrIp->in_addr)))
+ {
+ pvAddr = &pTdiAddrIp->in_addr;
+ enmAddrType = kIntNetAddrType_IPv4;
+ }
+ else
+ Log2(("vboxNetFltWinNotifyHostAddress: ignoring link-local address %RTnaipv4\n",
+ pTdiAddrIp->in_addr));
+ }
+ else if (pAddress->AddressType == TDI_ADDRESS_TYPE_IP6)
+ {
+ PTDI_ADDRESS_IP6 pTdiAddrIp6 = (PTDI_ADDRESS_IP6)pAddress->Address;
+ if (!vboxNetFltWinIsAddrLinkLocal6((PCRTNETADDRIPV6)(pTdiAddrIp6->sin6_addr)))
+ {
+ pvAddr = pTdiAddrIp6->sin6_addr;
+ enmAddrType = kIntNetAddrType_IPv6;
+ }
+ else
+ Log2(("vboxNetFltWinNotifyHostAddress: ignoring link-local address %RTnaipv6\n",
+ pTdiAddrIp6->sin6_addr));
+ }
+ else
+ {
+ Log2(("vboxNetFltWinNotifyHostAddress: ignoring irrelevant address type %d\n",
+ pAddress->AddressType));
+ LogFlow(("<==vboxNetFltWinNotifyHostAddress\n"));
+ return;
+ }
+ if (pvAddr)
+ {
+ NdisAcquireSpinLock(&g_VBoxNetFltGlobalsWin.lockFilters);
+ /* At this point the list must contain at least one element. */
+ PVBOXNETFLTINS pInstance = NULL;
+ PVBOXNETFLTWIN pFilter;
+ RTListForEach(&g_VBoxNetFltGlobalsWin.listFilters, pFilter, VBOXNETFLTWIN, node)
+ {
+ pInstance = RT_FROM_MEMBER(pFilter, VBOXNETFLTINS, u.s.WinIf);
+ if (vboxNetFltWinReferenceWinIf(pInstance))
+ {
+ if (pInstance->pSwitchPort && pInstance->pSwitchPort->pfnNotifyHostAddress)
+ break;
+ vboxNetFltWinDereferenceWinIf(pInstance);
+ }
+ else
+ Log2(("vboxNetFltWinNotifyHostAddress: failed to retain filter instance %p\n", pInstance));
+ pInstance = NULL;
+ }
+ NdisReleaseSpinLock(&g_VBoxNetFltGlobalsWin.lockFilters);
+ if (pInstance)
+ {
+ if (enmAddrType == kIntNetAddrType_IPv4)
+ Log2(("vboxNetFltWin%sAddressHandler: %RTnaipv4\n",
+ fAdded ? "Add" : "Del", *(PCRTNETADDRIPV4)pvAddr));
+ else
+ Log2(("vboxNetFltWin%sAddressHandler: %RTnaipv6\n",
+ fAdded ? "Add" : "Del", pvAddr));
+ pInstance->pSwitchPort->pfnNotifyHostAddress(pInstance->pSwitchPort, fAdded,
+ enmAddrType, pvAddr);
+ vboxNetFltWinDereferenceWinIf(pInstance);
+ }
+ else
+ Log2(("vboxNetFltWinNotifyHostAddress: no filters require notification\n"));
+ }
+ LogFlow(("<==vboxNetFltWinNotifyHostAddress\n"));
+}
+
+void vboxNetFltWinAddAddressHandler(PTA_ADDRESS Address,
+ PUNICODE_STRING DeviceName,
+ PTDI_PNP_CONTEXT Context)
+{
+ RT_NOREF2(DeviceName, Context);
+ vboxNetFltWinNotifyHostAddress(Address, true);
+}
+
+void vboxNetFltWinDelAddressHandler(PTA_ADDRESS Address,
+ PUNICODE_STRING DeviceName,
+ PTDI_PNP_CONTEXT Context)
+{
+ RT_NOREF2(DeviceName, Context);
+ vboxNetFltWinNotifyHostAddress(Address, false);
+}
+
+void vboxNetFltWinRegisterIpAddrNotifier(PVBOXNETFLTINS pThis)
+{
+ LogFlow(("==>vboxNetFltWinRegisterIpAddrNotifier: instance=%p pThis->pSwitchPort=%p pThis->pSwitchPort->pfnNotifyHostAddress=%p\n",
+ pThis, pThis->pSwitchPort, pThis->pSwitchPort ? pThis->pSwitchPort->pfnNotifyHostAddress : NULL));
+ if (pThis->pSwitchPort && pThis->pSwitchPort->pfnNotifyHostAddress)
+ {
+ NdisAcquireSpinLock(&g_VBoxNetFltGlobalsWin.lockFilters);
+ bool fRegisterHandlers = RTListIsEmpty(&g_VBoxNetFltGlobalsWin.listFilters);
+ RTListPrepend(&g_VBoxNetFltGlobalsWin.listFilters, &pThis->u.s.WinIf.node);
+ NdisReleaseSpinLock(&g_VBoxNetFltGlobalsWin.lockFilters);
+
+ if (fRegisterHandlers)
+ {
+ TDI_CLIENT_INTERFACE_INFO Info;
+ UNICODE_STRING ClientName = RTL_CONSTANT_STRING(L"VBoxNetFlt");
+ memset(&Info, 0, sizeof(Info));
+ Info.MajorTdiVersion = 2;
+ Info.MinorTdiVersion = 0;
+ Info.ClientName = &ClientName;
+ Info.AddAddressHandlerV2 = vboxNetFltWinAddAddressHandler;
+ Info.DelAddressHandlerV2 = vboxNetFltWinDelAddressHandler;
+ Assert(!g_VBoxNetFltGlobalsWin.hNotifier);
+ NTSTATUS Status = TdiRegisterPnPHandlers(&Info, sizeof(Info), &g_VBoxNetFltGlobalsWin.hNotifier);
+ Log2(("vboxNetFltWinRegisterIpAddrNotifier: TdiRegisterPnPHandlers returned %d\n", Status)); NOREF(Status);
+ }
+ else
+ Log2(("vboxNetFltWinRegisterIpAddrNotifier: already registed\n"));
+ }
+ else
+ Log2(("vboxNetFltWinRegisterIpAddrNotifier: this instance does not require notifications, ignoring...\n"));
+ LogFlow(("<==vboxNetFltWinRegisterIpAddrNotifier: notifier=%p\n", g_VBoxNetFltGlobalsWin.hNotifier));
+}
+
+void vboxNetFltWinUnregisterIpAddrNotifier(PVBOXNETFLTINS pThis)
+{
+ LogFlow(("==>vboxNetFltWinUnregisterIpAddrNotifier: notifier=%p\n", g_VBoxNetFltGlobalsWin.hNotifier));
+ if (pThis->pSwitchPort && pThis->pSwitchPort->pfnNotifyHostAddress)
+ {
+ NdisAcquireSpinLock(&g_VBoxNetFltGlobalsWin.lockFilters);
+ /* At this point the list must contain at least one element. */
+ Assert(!RTListIsEmpty(&g_VBoxNetFltGlobalsWin.listFilters));
+ RTListNodeRemove(&pThis->u.s.WinIf.node);
+ HANDLE hNotifier = NULL;
+ if (RTListIsEmpty(&g_VBoxNetFltGlobalsWin.listFilters))
+ {
+ /*
+ * The list has become empty, so we need to deregister handlers. We
+ * grab hNotifier and reset it while still holding the lock. This
+ * guaranties that we won't interfere with setting it in
+ * vboxNetFltWinRegisterIpAddrNotifier(). It is inconceivable that
+ * vboxNetFltWinUnregisterIpAddrNotifier() will be called for the
+ * same filter instance while it is still being processed by
+ * vboxNetFltWinRegisterIpAddrNotifier(). This would require trunk
+ * destruction in the middle of its creation. It is possible that
+ * vboxNetFltWinUnregisterIpAddrNotifier() is called for another
+ * filter instance, but in such case we won't even get here as the
+ * list won't be empty.
+ */
+ hNotifier = g_VBoxNetFltGlobalsWin.hNotifier;
+ g_VBoxNetFltGlobalsWin.hNotifier = NULL;
+ }
+ NdisReleaseSpinLock(&g_VBoxNetFltGlobalsWin.lockFilters);
+ if (hNotifier)
+ {
+ NTSTATUS Status = TdiDeregisterPnPHandlers(hNotifier);
+ Log2(("vboxNetFltWinUnregisterIpAddrNotifier: TdiDeregisterPnPHandlers(%p) returned %d\n",
+ hNotifier, Status)); NOREF(Status);
+ }
+ else
+ Log2(("vboxNetFltWinUnregisterIpAddrNotifier: filters remain, do not deregister handlers yet\n"));
+ }
+ else
+ Log2(("vboxNetFltWinUnregisterIpAddrNotifier: this instance did not require notifications, ignoring...\n"));
+ LogFlow(("<==vboxNetFltWinUnregisterIpAddrNotifier\n"));
+}
+#else /* VBOXNETADP */
+#define vboxNetFltWinRegisterIpAddrNotifier(x)
+#define vboxNetFltWinUnregisterIpAddrNotifier(x)
+#endif /* VBOXNETADP */
+
+int vboxNetFltOsDisconnectIt(PVBOXNETFLTINS pThis)
+{
+ NDIS_STATUS Status = vboxNetFltWinDisconnectIt(pThis);
+ Log2(("vboxNetFltOsDisconnectIt: pThis=%p pThis->pSwitchPort=%p pThis->pSwitchPort->pfnNotifyHostAddress=%p\n",
+ pThis, pThis->pSwitchPort, pThis->pSwitchPort ? pThis->pSwitchPort->pfnNotifyHostAddress : NULL));
+ vboxNetFltWinUnregisterIpAddrNotifier(pThis);
+ return Status == NDIS_STATUS_SUCCESS ? VINF_SUCCESS : VERR_GENERAL_FAILURE;
+}
+
+static void vboxNetFltWinConnectItWorker(PVOID pvContext)
+{
+ PWORKER_INFO pInfo = (PWORKER_INFO)pvContext;
+#if !defined(VBOXNETADP) || !defined(VBOXNETFLT_NO_PACKET_QUEUE)
+ NDIS_STATUS Status;
+#endif
+ PVBOXNETFLTINS pInstance = pInfo->pNetFltIf;
+
+ Assert(KeGetCurrentIrql() == PASSIVE_LEVEL);
+
+ /* this is not a rediscovery, initialize Mac cache */
+ if (vboxNetFltWinReferenceWinIf(pInstance))
+ {
+#ifndef VBOXNETADP
+ Status = vboxNetFltWinGetMacAddress(pInstance, &pInstance->u.s.MacAddr);
+ if (Status == NDIS_STATUS_SUCCESS)
+#endif
+ {
+#ifdef VBOXNETFLT_NO_PACKET_QUEUE
+ pInfo->Status = VINF_SUCCESS;
+#else
+ Status = vboxNetFltWinQuInitPacketQueue(pInstance);
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ pInfo->Status = VINF_SUCCESS;
+ }
+ else
+ {
+ pInfo->Status = VERR_GENERAL_FAILURE;
+ }
+#endif
+ }
+#ifndef VBOXNETADP
+ else
+ {
+ pInfo->Status = VERR_INTNET_FLT_IF_FAILED;
+ }
+#endif
+
+ vboxNetFltWinDereferenceWinIf(pInstance);
+ }
+ else
+ {
+ pInfo->Status = VERR_INTNET_FLT_IF_NOT_FOUND;
+ }
+}
+
+static int vboxNetFltWinConnectIt(PVBOXNETFLTINS pThis)
+{
+ WORKER_INFO Info;
+ Info.pNetFltIf = pThis;
+
+ vboxNetFltWinJobSynchExecAtPassive(vboxNetFltWinConnectItWorker, &Info);
+
+ if (RT_SUCCESS(Info.Status))
+ vboxNetFltWinReportStuff(pThis);
+
+ return Info.Status;
+}
+
+int vboxNetFltOsConnectIt(PVBOXNETFLTINS pThis)
+{
+ Log2(("vboxNetFltOsConnectIt: pThis=%p pThis->pSwitchPort=%p pThis->pSwitchPort->pfnNotifyHostAddress=%p\n",
+ pThis, pThis->pSwitchPort, pThis->pSwitchPort ? pThis->pSwitchPort->pfnNotifyHostAddress : NULL));
+ vboxNetFltWinRegisterIpAddrNotifier(pThis);
+ return vboxNetFltWinConnectIt(pThis);
+}
+
+void vboxNetFltOsDeleteInstance(PVBOXNETFLTINS pThis)
+{
+ vboxNetFltWinDeleteInstance(pThis);
+}
+
+int vboxNetFltOsInitInstance(PVBOXNETFLTINS pThis, void *pvContext)
+{
+ int rc = RTSemMutexCreate(&pThis->u.s.hWinIfMutex);
+ if (RT_SUCCESS(rc))
+ {
+ rc = vboxNetFltWinAttachToInterface(pThis, pvContext, false /*fRediscovery*/ );
+ if (RT_SUCCESS(rc))
+ {
+ return rc;
+ }
+ RTSemMutexDestroy(pThis->u.s.hWinIfMutex);
+ }
+ return rc;
+}
+
+int vboxNetFltOsPreInitInstance(PVBOXNETFLTINS pThis)
+{
+ pThis->u.s.cModeNetFltRefs = 0;
+ pThis->u.s.cModePassThruRefs = 0;
+ vboxNetFltWinSetWinIfState(pThis, kVBoxWinIfState_Disconnected);
+ vboxNetFltWinSetOpState(&pThis->u.s.WinIf.MpState, kVBoxNetDevOpState_Deinitialized);
+#ifndef VBOXNETADP
+ vboxNetFltWinSetOpState(&pThis->u.s.WinIf.PtState, kVBoxNetDevOpState_Deinitialized);
+#endif
+ return VINF_SUCCESS;
+}
+
+void vboxNetFltPortOsNotifyMacAddress(PVBOXNETFLTINS pThis, void *pvIfData, PCRTMAC pMac)
+{
+ RT_NOREF3(pThis, pvIfData, pMac);
+}
+
+int vboxNetFltPortOsConnectInterface(PVBOXNETFLTINS pThis, void *pvIf, void **ppvIfData)
+{
+ /* Nothing to do */
+ RT_NOREF3(pThis, pvIf, ppvIfData);
+ return VINF_SUCCESS;
+}
+
+int vboxNetFltPortOsDisconnectInterface(PVBOXNETFLTINS pThis, void *pvIfData)
+{
+ /* Nothing to do */
+ RT_NOREF2(pThis, pvIfData);
+ return VINF_SUCCESS;
+}
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltRt-win.h b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltRt-win.h
new file mode 100644
index 00000000..36235409
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltRt-win.h
@@ -0,0 +1,972 @@
+/* $Id: VBoxNetFltRt-win.h $ */
+/** @file
+ * VBoxNetFltRt-win.h - Bridged Networking Driver, Windows Specific Code.
+ * NetFlt Runtime API
+ */
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_SRC_VBoxNetFlt_win_drv_VBoxNetFltRt_win_h
+#define VBOX_INCLUDED_SRC_VBoxNetFlt_win_drv_VBoxNetFltRt_win_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+DECLHIDDEN(VOID) vboxNetFltWinUnload(IN PDRIVER_OBJECT DriverObject);
+
+#ifndef VBOXNETADP
+# if !defined(VBOX_LOOPBACK_USEFLAGS) || defined(DEBUG_NETFLT_PACKETS)
+DECLHIDDEN(bool) vboxNetFltWinMatchPackets(PNDIS_PACKET pPacket1, PNDIS_PACKET pPacket2, const INT cbMatch);
+DECLHIDDEN(bool) vboxNetFltWinMatchPacketAndSG(PNDIS_PACKET pPacket, PINTNETSG pSG, const INT cbMatch);
+# endif
+#endif
+
+/*************************
+ * packet queue API *
+ *************************/
+
+
+#define LIST_ENTRY_2_PACKET_INFO(pListEntry) \
+ ( (PVBOXNETFLT_PACKET_INFO)((uint8_t *)(pListEntry) - RT_UOFFSETOF(VBOXNETFLT_PACKET_INFO, ListEntry)) )
+
+#if !defined(VBOX_LOOPBACK_USEFLAGS) || defined(DEBUG_NETFLT_PACKETS)
+
+#define VBOX_SLE_2_PKTRSVD_PT(_pEntry) \
+ ( (PVBOXNETFLT_PKTRSVD_PT)((uint8_t *)(_pEntry) - RT_UOFFSETOF(VBOXNETFLT_PKTRSVD_PT, ListEntry)) )
+
+#define VBOX_SLE_2_SENDPACKET(_pEntry) \
+ ( (PNDIS_PACKET)((uint8_t *)(VBOX_SLE_2_PKTRSVD_PT(_pEntry)) - RT_UOFFSETOF(NDIS_PACKET, ProtocolReserved)) )
+
+#endif
+/**
+ * enqueus the packet info to the tail of the queue
+ */
+DECLINLINE(void) vboxNetFltWinQuEnqueueTail(PVBOXNETFLT_PACKET_QUEUE pQueue, PVBOXNETFLT_PACKET_INFO pPacketInfo)
+{
+ InsertTailList(pQueue, &pPacketInfo->ListEntry);
+}
+
+DECLINLINE(void) vboxNetFltWinQuEnqueueHead(PVBOXNETFLT_PACKET_QUEUE pQueue, PVBOXNETFLT_PACKET_INFO pPacketInfo)
+{
+ Assert(pPacketInfo->pPool);
+ InsertHeadList(pQueue, &pPacketInfo->ListEntry);
+}
+
+/**
+ * enqueus the packet info to the tail of the queue
+ */
+DECLINLINE(void) vboxNetFltWinQuInterlockedEnqueueTail(PVBOXNETFLT_INTERLOCKED_PACKET_QUEUE pQueue, PVBOXNETFLT_PACKET_INFO pPacketInfo)
+{
+ Assert(pPacketInfo->pPool);
+ NdisAcquireSpinLock(&pQueue->Lock);
+ vboxNetFltWinQuEnqueueTail(&pQueue->Queue, pPacketInfo);
+ NdisReleaseSpinLock(&pQueue->Lock);
+}
+
+DECLINLINE(void) vboxNetFltWinQuInterlockedEnqueueHead(PVBOXNETFLT_INTERLOCKED_PACKET_QUEUE pQueue, PVBOXNETFLT_PACKET_INFO pPacketInfo)
+{
+ NdisAcquireSpinLock(&pQueue->Lock);
+ vboxNetFltWinQuEnqueueHead(&pQueue->Queue, pPacketInfo);
+ NdisReleaseSpinLock(&pQueue->Lock);
+}
+
+/**
+ * dequeus the packet info from the head of the queue
+ */
+DECLINLINE(PVBOXNETFLT_PACKET_INFO) vboxNetFltWinQuDequeueHead(PVBOXNETFLT_PACKET_QUEUE pQueue)
+{
+ PLIST_ENTRY pListEntry = RemoveHeadList(pQueue);
+ if (pListEntry != pQueue)
+ {
+ PVBOXNETFLT_PACKET_INFO pInfo = LIST_ENTRY_2_PACKET_INFO(pListEntry);
+ Assert(pInfo->pPool);
+ return pInfo;
+ }
+ return NULL;
+}
+
+DECLINLINE(PVBOXNETFLT_PACKET_INFO) vboxNetFltWinQuDequeueTail(PVBOXNETFLT_PACKET_QUEUE pQueue)
+{
+ PLIST_ENTRY pListEntry = RemoveTailList(pQueue);
+ if (pListEntry != pQueue)
+ {
+ PVBOXNETFLT_PACKET_INFO pInfo = LIST_ENTRY_2_PACKET_INFO(pListEntry);
+ Assert(pInfo->pPool);
+ return pInfo;
+ }
+ return NULL;
+}
+
+DECLINLINE(PVBOXNETFLT_PACKET_INFO) vboxNetFltWinQuInterlockedDequeueHead(PVBOXNETFLT_INTERLOCKED_PACKET_QUEUE pInterlockedQueue)
+{
+ PVBOXNETFLT_PACKET_INFO pInfo;
+ NdisAcquireSpinLock(&pInterlockedQueue->Lock);
+ pInfo = vboxNetFltWinQuDequeueHead(&pInterlockedQueue->Queue);
+ NdisReleaseSpinLock(&pInterlockedQueue->Lock);
+ return pInfo;
+}
+
+DECLINLINE(PVBOXNETFLT_PACKET_INFO) vboxNetFltWinQuInterlockedDequeueTail(PVBOXNETFLT_INTERLOCKED_PACKET_QUEUE pInterlockedQueue)
+{
+ PVBOXNETFLT_PACKET_INFO pInfo;
+ NdisAcquireSpinLock(&pInterlockedQueue->Lock);
+ pInfo = vboxNetFltWinQuDequeueTail(&pInterlockedQueue->Queue);
+ NdisReleaseSpinLock(&pInterlockedQueue->Lock);
+ return pInfo;
+}
+
+DECLINLINE(void) vboxNetFltWinQuDequeue(PVBOXNETFLT_PACKET_INFO pInfo)
+{
+ RemoveEntryList(&pInfo->ListEntry);
+}
+
+DECLINLINE(void) vboxNetFltWinQuInterlockedDequeue(PVBOXNETFLT_INTERLOCKED_PACKET_QUEUE pInterlockedQueue, PVBOXNETFLT_PACKET_INFO pInfo)
+{
+ NdisAcquireSpinLock(&pInterlockedQueue->Lock);
+ vboxNetFltWinQuDequeue(pInfo);
+ NdisReleaseSpinLock(&pInterlockedQueue->Lock);
+}
+
+/**
+ * allocates the packet info from the pool
+ */
+DECLINLINE(PVBOXNETFLT_PACKET_INFO) vboxNetFltWinPpAllocPacketInfo(PVBOXNETFLT_PACKET_INFO_POOL pPool)
+{
+ return vboxNetFltWinQuInterlockedDequeueHead(&pPool->Queue);
+}
+
+/**
+ * returns the packet info to the pool
+ */
+DECLINLINE(void) vboxNetFltWinPpFreePacketInfo(PVBOXNETFLT_PACKET_INFO pInfo)
+{
+ PVBOXNETFLT_PACKET_INFO_POOL pPool = pInfo->pPool;
+ vboxNetFltWinQuInterlockedEnqueueHead(&pPool->Queue, pInfo);
+}
+
+/** initializes the packet queue */
+#define INIT_PACKET_QUEUE(_pQueue) InitializeListHead((_pQueue))
+
+/** initializes the packet queue */
+#define INIT_INTERLOCKED_PACKET_QUEUE(_pQueue) \
+ { \
+ INIT_PACKET_QUEUE(&(_pQueue)->Queue); \
+ NdisAllocateSpinLock(&(_pQueue)->Lock); \
+ }
+
+/** delete the packet queue */
+#define FINI_INTERLOCKED_PACKET_QUEUE(_pQueue) NdisFreeSpinLock(&(_pQueue)->Lock)
+
+/** returns the packet the packet info contains */
+#define GET_PACKET_FROM_INFO(_pPacketInfo) (ASMAtomicUoReadPtr((void * volatile *)&(_pPacketInfo)->pPacket))
+
+/** assignes the packet to the packet info */
+#define SET_PACKET_TO_INFO(_pPacketInfo, _pPacket) (ASMAtomicUoWritePtr(&(_pPacketInfo)->pPacket, (_pPacket)))
+
+/** returns the flags the packet info contains */
+#define GET_FLAGS_FROM_INFO(_pPacketInfo) (ASMAtomicUoReadU32((volatile uint32_t *)&(_pPacketInfo)->fFlags))
+
+/** sets flags to the packet info */
+#define SET_FLAGS_TO_INFO(_pPacketInfo, _fFlags) (ASMAtomicUoWriteU32((volatile uint32_t *)&(_pPacketInfo)->fFlags, (_fFlags)))
+
+#ifdef VBOXNETFLT_NO_PACKET_QUEUE
+DECLHIDDEN(bool) vboxNetFltWinPostIntnet(PVBOXNETFLTINS pInstance, PVOID pvPacket, const UINT fFlags);
+#else
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinQuEnqueuePacket(PVBOXNETFLTINS pInstance, PVOID pPacket, const UINT fPacketFlags);
+DECLHIDDEN(void) vboxNetFltWinQuFiniPacketQueue(PVBOXNETFLTINS pInstance);
+DECLHIDDEN(NTSTATUS) vboxNetFltWinQuInitPacketQueue(PVBOXNETFLTINS pInstance);
+#endif /* #ifndef VBOXNETFLT_NO_PACKET_QUEUE */
+
+
+#ifndef VBOXNETADP
+/**
+ * searches the list entry in a single-linked list
+ */
+DECLINLINE(bool) vboxNetFltWinSearchListEntry(PVBOXNETFLT_SINGLE_LIST pList, PSINGLE_LIST_ENTRY pEntry2Search, bool bRemove)
+{
+ PSINGLE_LIST_ENTRY pHead = &pList->Head;
+ PSINGLE_LIST_ENTRY pCur;
+ PSINGLE_LIST_ENTRY pPrev;
+ for (pCur = pHead->Next, pPrev = pHead; pCur; pPrev = pCur, pCur = pCur->Next)
+ {
+ if (pEntry2Search == pCur)
+ {
+ if (bRemove)
+ {
+ pPrev->Next = pCur->Next;
+ if (pCur == pList->pTail)
+ {
+ pList->pTail = pPrev;
+ }
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+#if !defined(VBOX_LOOPBACK_USEFLAGS) || defined(DEBUG_NETFLT_PACKETS)
+
+DECLINLINE(PNDIS_PACKET) vboxNetFltWinSearchPacket(PVBOXNETFLT_SINGLE_LIST pList, PNDIS_PACKET pPacket2Search, int cbMatch, bool bRemove)
+{
+ PSINGLE_LIST_ENTRY pHead = &pList->Head;
+ PSINGLE_LIST_ENTRY pCur;
+ PSINGLE_LIST_ENTRY pPrev;
+ PNDIS_PACKET pCurPacket;
+ for (pCur = pHead->Next, pPrev = pHead; pCur; pPrev = pCur, pCur = pCur->Next)
+ {
+ pCurPacket = VBOX_SLE_2_SENDPACKET(pCur);
+ if (pCurPacket == pPacket2Search || vboxNetFltWinMatchPackets(pPacket2Search, pCurPacket, cbMatch))
+ {
+ if (bRemove)
+ {
+ pPrev->Next = pCur->Next;
+ if (pCur == pList->pTail)
+ {
+ pList->pTail = pPrev;
+ }
+ }
+ return pCurPacket;
+ }
+ }
+ return NULL;
+}
+
+DECLINLINE(PNDIS_PACKET) vboxNetFltWinSearchPacketBySG(PVBOXNETFLT_SINGLE_LIST pList, PINTNETSG pSG, int cbMatch, bool bRemove)
+{
+ PSINGLE_LIST_ENTRY pHead = &pList->Head;
+ PSINGLE_LIST_ENTRY pCur;
+ PSINGLE_LIST_ENTRY pPrev;
+ PNDIS_PACKET pCurPacket;
+ for (pCur = pHead->Next, pPrev = pHead; pCur; pPrev = pCur, pCur = pCur->Next)
+ {
+ pCurPacket = VBOX_SLE_2_SENDPACKET(pCur);
+ if (vboxNetFltWinMatchPacketAndSG(pCurPacket, pSG, cbMatch))
+ {
+ if (bRemove)
+ {
+ pPrev->Next = pCur->Next;
+ if (pCur == pList->pTail)
+ {
+ pList->pTail = pPrev;
+ }
+ }
+ return pCurPacket;
+ }
+ }
+ return NULL;
+}
+
+#endif /* #if !defined(VBOX_LOOPBACK_USEFLAGS) || defined(DEBUG_NETFLT_PACKETS) */
+
+DECLINLINE(bool) vboxNetFltWinSListIsEmpty(PVBOXNETFLT_SINGLE_LIST pList)
+{
+ return !pList->Head.Next;
+}
+
+DECLINLINE(void) vboxNetFltWinPutTail(PVBOXNETFLT_SINGLE_LIST pList, PSINGLE_LIST_ENTRY pEntry)
+{
+ pList->pTail->Next = pEntry;
+ pList->pTail = pEntry;
+ pEntry->Next = NULL;
+}
+
+DECLINLINE(void) vboxNetFltWinPutHead(PVBOXNETFLT_SINGLE_LIST pList, PSINGLE_LIST_ENTRY pEntry)
+{
+ pEntry->Next = pList->Head.Next;
+ pList->Head.Next = pEntry;
+ if (!pEntry->Next)
+ pList->pTail = pEntry;
+}
+
+DECLINLINE(PSINGLE_LIST_ENTRY) vboxNetFltWinGetHead(PVBOXNETFLT_SINGLE_LIST pList)
+{
+ PSINGLE_LIST_ENTRY pEntry = pList->Head.Next;
+ if (pEntry && pEntry == pList->pTail)
+ {
+ pList->Head.Next = NULL;
+ pList->pTail = &pList->Head;
+ }
+ return pEntry;
+}
+
+DECLINLINE(bool) vboxNetFltWinInterlockedSearchListEntry(PVBOXNETFLT_INTERLOCKED_SINGLE_LIST pList, PSINGLE_LIST_ENTRY pEntry2Search, bool bRemove)
+{
+ bool bFound;
+ NdisAcquireSpinLock(&pList->Lock);
+ bFound = vboxNetFltWinSearchListEntry(&pList->List, pEntry2Search, bRemove);
+ NdisReleaseSpinLock(&pList->Lock);
+ return bFound;
+}
+
+#if !defined(VBOX_LOOPBACK_USEFLAGS) || defined(DEBUG_NETFLT_PACKETS)
+
+DECLINLINE(PNDIS_PACKET) vboxNetFltWinInterlockedSearchPacket(PVBOXNETFLT_INTERLOCKED_SINGLE_LIST pList, PNDIS_PACKET pPacket2Search, int cbMatch, bool bRemove)
+{
+ PNDIS_PACKET pFound;
+ NdisAcquireSpinLock(&pList->Lock);
+ pFound = vboxNetFltWinSearchPacket(&pList->List, pPacket2Search, cbMatch, bRemove);
+ NdisReleaseSpinLock(&pList->Lock);
+ return pFound;
+}
+
+DECLINLINE(PNDIS_PACKET) vboxNetFltWinInterlockedSearchPacketBySG(PVBOXNETFLT_INTERLOCKED_SINGLE_LIST pList, PINTNETSG pSG, int cbMatch, bool bRemove)
+{
+ PNDIS_PACKET pFound;
+ NdisAcquireSpinLock(&pList->Lock);
+ pFound = vboxNetFltWinSearchPacketBySG(&pList->List, pSG, cbMatch, bRemove);
+ NdisReleaseSpinLock(&pList->Lock);
+ return pFound;
+}
+#endif /* #if !defined(VBOX_LOOPBACK_USEFLAGS) || defined(DEBUG_NETFLT_PACKETS) */
+
+DECLINLINE(void) vboxNetFltWinInterlockedPutTail(PVBOXNETFLT_INTERLOCKED_SINGLE_LIST pList, PSINGLE_LIST_ENTRY pEntry)
+{
+ NdisAcquireSpinLock(&pList->Lock);
+ vboxNetFltWinPutTail(&pList->List, pEntry);
+ NdisReleaseSpinLock(&pList->Lock);
+}
+
+DECLINLINE(void) vboxNetFltWinInterlockedPutHead(PVBOXNETFLT_INTERLOCKED_SINGLE_LIST pList, PSINGLE_LIST_ENTRY pEntry)
+{
+ NdisAcquireSpinLock(&pList->Lock);
+ vboxNetFltWinPutHead(&pList->List, pEntry);
+ NdisReleaseSpinLock(&pList->Lock);
+}
+
+DECLINLINE(PSINGLE_LIST_ENTRY) vboxNetFltWinInterlockedGetHead(PVBOXNETFLT_INTERLOCKED_SINGLE_LIST pList)
+{
+ PSINGLE_LIST_ENTRY pEntry;
+ NdisAcquireSpinLock(&pList->Lock);
+ pEntry = vboxNetFltWinGetHead(&pList->List);
+ NdisReleaseSpinLock(&pList->Lock);
+ return pEntry;
+}
+
+# if defined(DEBUG_NETFLT_PACKETS) || !defined(VBOX_LOOPBACK_USEFLAGS)
+DECLINLINE(void) vboxNetFltWinLbPutSendPacket(PVBOXNETFLTINS pNetFlt, PNDIS_PACKET pPacket, bool bFromIntNet)
+{
+ PVBOXNETFLT_PKTRSVD_PT pSrv = (PVBOXNETFLT_PKTRSVD_PT)pPacket->ProtocolReserved;
+ pSrv->bFromIntNet = bFromIntNet;
+ vboxNetFltWinInterlockedPutHead(&pNetFlt->u.s.WinIf.SendPacketQueue, &pSrv->ListEntry);
+}
+
+DECLINLINE(bool) vboxNetFltWinLbIsFromIntNet(PNDIS_PACKET pPacket)
+{
+ PVBOXNETFLT_PKTRSVD_PT pSrv = (PVBOXNETFLT_PKTRSVD_PT)pPacket->ProtocolReserved;
+ return pSrv->bFromIntNet;
+}
+
+DECLINLINE(PNDIS_PACKET) vboxNetFltWinLbSearchLoopBack(PVBOXNETFLTINS pNetFlt, PNDIS_PACKET pPacket, bool bRemove)
+{
+ return vboxNetFltWinInterlockedSearchPacket(&pNetFlt->u.s.WinIf.SendPacketQueue, pPacket, VBOXNETFLT_PACKETMATCH_LENGTH, bRemove);
+}
+
+DECLINLINE(PNDIS_PACKET) vboxNetFltWinLbSearchLoopBackBySG(PVBOXNETFLTINS pNetFlt, PINTNETSG pSG, bool bRemove)
+{
+ return vboxNetFltWinInterlockedSearchPacketBySG(&pNetFlt->u.s.WinIf.SendPacketQueue, pSG, VBOXNETFLT_PACKETMATCH_LENGTH, bRemove);
+}
+
+DECLINLINE(bool) vboxNetFltWinLbRemoveSendPacket(PVBOXNETFLTINS pNetFlt, PNDIS_PACKET pPacket)
+{
+ PVBOXNETFLT_PKTRSVD_PT pSrv = (PVBOXNETFLT_PKTRSVD_PT)pPacket->ProtocolReserved;
+ bool bRet = vboxNetFltWinInterlockedSearchListEntry(&pNetFlt->u.s.WinIf.SendPacketQueue, &pSrv->ListEntry, true);
+#ifdef DEBUG_misha
+ Assert(bRet == (pNetFlt->enmTrunkState == INTNETTRUNKIFSTATE_ACTIVE));
+#endif
+ return bRet;
+}
+
+# endif
+
+#endif
+
+#ifdef DEBUG_misha
+DECLHIDDEN(bool) vboxNetFltWinCheckMACs(PNDIS_PACKET pPacket, PRTMAC pDst, PRTMAC pSrc);
+DECLHIDDEN(bool) vboxNetFltWinCheckMACsSG(PINTNETSG pSG, PRTMAC pDst, PRTMAC pSrc);
+extern RTMAC g_vboxNetFltWinVerifyMACBroadcast;
+extern RTMAC g_vboxNetFltWinVerifyMACGuest;
+
+# define VBOXNETFLT_LBVERIFY(_pnf, _p) \
+ do { \
+ Assert(!vboxNetFltWinCheckMACs(_p, NULL, &g_vboxNetFltWinVerifyMACGuest)); \
+ Assert(!vboxNetFltWinCheckMACs(_p, NULL, &(_pnf)->u.s.MacAddr)); \
+ } while (0)
+
+# define VBOXNETFLT_LBVERIFYSG(_pnf, _p) \
+ do { \
+ Assert(!vboxNetFltWinCheckMACsSG(_p, NULL, &g_vboxNetFltWinVerifyMACGuest)); \
+ Assert(!vboxNetFltWinCheckMACsSG(_p, NULL, &(_pnf)->u.s.MacAddr)); \
+ } while (0)
+
+#else
+# define VBOXNETFLT_LBVERIFY(_pnf, _p) do { } while (0)
+# define VBOXNETFLT_LBVERIFYSG(_pnf, _p) do { } while (0)
+#endif
+
+/** initializes the list */
+#define INIT_SINGLE_LIST(_pList) \
+ { \
+ (_pList)->Head.Next = NULL; \
+ (_pList)->pTail = &(_pList)->Head; \
+ }
+
+/** initializes the list */
+#define INIT_INTERLOCKED_SINGLE_LIST(_pList) \
+ do { \
+ INIT_SINGLE_LIST(&(_pList)->List); \
+ NdisAllocateSpinLock(&(_pList)->Lock); \
+ } while (0)
+
+/** delete the packet queue */
+#define FINI_INTERLOCKED_SINGLE_LIST(_pList) \
+ do { \
+ Assert(vboxNetFltWinSListIsEmpty(&(_pList)->List)); \
+ NdisFreeSpinLock(&(_pList)->Lock); \
+ } while (0)
+
+
+/**************************************************************************
+ * PVBOXNETFLTINS , WinIf reference/dereference (i.e. retain/release) API *
+ **************************************************************************/
+
+
+DECLHIDDEN(void) vboxNetFltWinWaitDereference(PVBOXNETFLT_WINIF_DEVICE pState);
+
+DECLINLINE(void) vboxNetFltWinReferenceModeNetFlt(PVBOXNETFLTINS pIns)
+{
+ ASMAtomicIncU32((volatile uint32_t *)&pIns->u.s.cModeNetFltRefs);
+}
+
+DECLINLINE(void) vboxNetFltWinReferenceModePassThru(PVBOXNETFLTINS pIns)
+{
+ ASMAtomicIncU32((volatile uint32_t *)&pIns->u.s.cModePassThruRefs);
+}
+
+DECLINLINE(void) vboxNetFltWinIncReferenceModeNetFlt(PVBOXNETFLTINS pIns, uint32_t v)
+{
+ ASMAtomicAddU32((volatile uint32_t *)&pIns->u.s.cModeNetFltRefs, v);
+}
+
+DECLINLINE(void) vboxNetFltWinIncReferenceModePassThru(PVBOXNETFLTINS pIns, uint32_t v)
+{
+ ASMAtomicAddU32((volatile uint32_t *)&pIns->u.s.cModePassThruRefs, v);
+}
+
+DECLINLINE(void) vboxNetFltWinDereferenceModeNetFlt(PVBOXNETFLTINS pIns)
+{
+ ASMAtomicDecU32((volatile uint32_t *)&pIns->u.s.cModeNetFltRefs);
+}
+
+DECLINLINE(void) vboxNetFltWinDereferenceModePassThru(PVBOXNETFLTINS pIns)
+{
+ ASMAtomicDecU32((volatile uint32_t *)&pIns->u.s.cModePassThruRefs);
+}
+
+DECLINLINE(void) vboxNetFltWinDecReferenceModeNetFlt(PVBOXNETFLTINS pIns, uint32_t v)
+{
+ Assert(v);
+ ASMAtomicAddU32((volatile uint32_t *)&pIns->u.s.cModeNetFltRefs, (uint32_t)(-((int32_t)v)));
+}
+
+DECLINLINE(void) vboxNetFltWinDecReferenceModePassThru(PVBOXNETFLTINS pIns, uint32_t v)
+{
+ Assert(v);
+ ASMAtomicAddU32((volatile uint32_t *)&pIns->u.s.cModePassThruRefs, (uint32_t)(-((int32_t)v)));
+}
+
+DECLINLINE(void) vboxNetFltWinSetPowerState(PVBOXNETFLT_WINIF_DEVICE pState, NDIS_DEVICE_POWER_STATE State)
+{
+ ASMAtomicUoWriteU32((volatile uint32_t *)&pState->PowerState, State);
+}
+
+DECLINLINE(NDIS_DEVICE_POWER_STATE) vboxNetFltWinGetPowerState(PVBOXNETFLT_WINIF_DEVICE pState)
+{
+ return (NDIS_DEVICE_POWER_STATE)ASMAtomicUoReadU32((volatile uint32_t *)&pState->PowerState);
+}
+
+DECLINLINE(void) vboxNetFltWinSetOpState(PVBOXNETFLT_WINIF_DEVICE pState, VBOXNETDEVOPSTATE State)
+{
+ ASMAtomicUoWriteU32((volatile uint32_t *)&pState->OpState, State);
+}
+
+DECLINLINE(VBOXNETDEVOPSTATE) vboxNetFltWinGetOpState(PVBOXNETFLT_WINIF_DEVICE pState)
+{
+ return (VBOXNETDEVOPSTATE)ASMAtomicUoReadU32((volatile uint32_t *)&pState->OpState);
+}
+
+DECLINLINE(bool) vboxNetFltWinDoReferenceDevice(PVBOXNETFLT_WINIF_DEVICE pState)
+{
+ if (vboxNetFltWinGetPowerState(pState) == NdisDeviceStateD0 && vboxNetFltWinGetOpState(pState) == kVBoxNetDevOpState_Initialized)
+ {
+ /** @todo r=bird: Since this is a volatile member, why don't you declare it as
+ * such and save yourself all the casting? */
+ ASMAtomicIncU32((uint32_t volatile *)&pState->cReferences);
+ return true;
+ }
+ return false;
+}
+
+#ifndef VBOXNETADP
+DECLINLINE(bool) vboxNetFltWinDoReferenceDevices(PVBOXNETFLT_WINIF_DEVICE pState1, PVBOXNETFLT_WINIF_DEVICE pState2)
+{
+ if (vboxNetFltWinGetPowerState(pState1) == NdisDeviceStateD0
+ && vboxNetFltWinGetOpState(pState1) == kVBoxNetDevOpState_Initialized
+ && vboxNetFltWinGetPowerState(pState2) == NdisDeviceStateD0
+ && vboxNetFltWinGetOpState(pState2) == kVBoxNetDevOpState_Initialized)
+ {
+ ASMAtomicIncU32((uint32_t volatile *)&pState1->cReferences);
+ ASMAtomicIncU32((uint32_t volatile *)&pState2->cReferences);
+ return true;
+ }
+ return false;
+}
+#endif
+
+DECLINLINE(void) vboxNetFltWinDereferenceDevice(PVBOXNETFLT_WINIF_DEVICE pState)
+{
+ ASMAtomicDecU32((uint32_t volatile *)&pState->cReferences);
+ /** @todo r=bird: Add comment explaining why these cannot hit 0 or why
+ * reference are counted */
+}
+
+#ifndef VBOXNETADP
+DECLINLINE(void) vboxNetFltWinDereferenceDevices(PVBOXNETFLT_WINIF_DEVICE pState1, PVBOXNETFLT_WINIF_DEVICE pState2)
+{
+ ASMAtomicDecU32((uint32_t volatile *)&pState1->cReferences);
+ ASMAtomicDecU32((uint32_t volatile *)&pState2->cReferences);
+}
+#endif
+
+DECLINLINE(void) vboxNetFltWinDecReferenceDevice(PVBOXNETFLT_WINIF_DEVICE pState, uint32_t v)
+{
+ Assert(v);
+ ASMAtomicAddU32((uint32_t volatile *)&pState->cReferences, (uint32_t)(-((int32_t)v)));
+}
+
+#ifndef VBOXNETADP
+DECLINLINE(void) vboxNetFltWinDecReferenceDevices(PVBOXNETFLT_WINIF_DEVICE pState1, PVBOXNETFLT_WINIF_DEVICE pState2, uint32_t v)
+{
+ ASMAtomicAddU32((uint32_t volatile *)&pState1->cReferences, (uint32_t)(-((int32_t)v)));
+ ASMAtomicAddU32((uint32_t volatile *)&pState2->cReferences, (uint32_t)(-((int32_t)v)));
+}
+#endif
+
+DECLINLINE(bool) vboxNetFltWinDoIncReferenceDevice(PVBOXNETFLT_WINIF_DEVICE pState, uint32_t v)
+{
+ Assert(v);
+ if (vboxNetFltWinGetPowerState(pState) == NdisDeviceStateD0 && vboxNetFltWinGetOpState(pState) == kVBoxNetDevOpState_Initialized)
+ {
+ ASMAtomicAddU32((uint32_t volatile *)&pState->cReferences, v);
+ return true;
+ }
+ return false;
+}
+
+#ifndef VBOXNETADP
+DECLINLINE(bool) vboxNetFltWinDoIncReferenceDevices(PVBOXNETFLT_WINIF_DEVICE pState1, PVBOXNETFLT_WINIF_DEVICE pState2, uint32_t v)
+{
+ if (vboxNetFltWinGetPowerState(pState1) == NdisDeviceStateD0
+ && vboxNetFltWinGetOpState(pState1) == kVBoxNetDevOpState_Initialized
+ && vboxNetFltWinGetPowerState(pState2) == NdisDeviceStateD0
+ && vboxNetFltWinGetOpState(pState2) == kVBoxNetDevOpState_Initialized)
+ {
+ ASMAtomicAddU32((uint32_t volatile *)&pState1->cReferences, v);
+ ASMAtomicAddU32((uint32_t volatile *)&pState2->cReferences, v);
+ return true;
+ }
+ return false;
+}
+#endif
+
+
+DECLINLINE(bool) vboxNetFltWinReferenceWinIfNetFlt(PVBOXNETFLTINS pNetFlt, bool * pbNetFltActive)
+{
+ RTSpinlockAcquire((pNetFlt)->hSpinlock);
+#ifndef VBOXNETADP
+ if (!vboxNetFltWinDoReferenceDevices(&pNetFlt->u.s.WinIf.MpState, &pNetFlt->u.s.WinIf.PtState))
+#else
+ if (!vboxNetFltWinDoReferenceDevice(&pNetFlt->u.s.WinIf.MpState))
+#endif
+ {
+ RTSpinlockRelease((pNetFlt)->hSpinlock);
+ *pbNetFltActive = false;
+ return false;
+ }
+
+ if (pNetFlt->enmTrunkState != INTNETTRUNKIFSTATE_ACTIVE)
+ {
+ vboxNetFltWinReferenceModePassThru(pNetFlt);
+ RTSpinlockRelease((pNetFlt)->hSpinlock);
+ *pbNetFltActive = false;
+ return true;
+ }
+
+ vboxNetFltRetain((pNetFlt), true /* fBusy */);
+ vboxNetFltWinReferenceModeNetFlt(pNetFlt);
+ RTSpinlockRelease((pNetFlt)->hSpinlock);
+
+ *pbNetFltActive = true;
+ return true;
+}
+
+DECLINLINE(bool) vboxNetFltWinIncReferenceWinIfNetFlt(PVBOXNETFLTINS pNetFlt, uint32_t v, bool *pbNetFltActive)
+{
+ uint32_t i;
+
+ Assert(v);
+ if (!v)
+ {
+ *pbNetFltActive = false;
+ return false;
+ }
+
+ RTSpinlockAcquire((pNetFlt)->hSpinlock);
+#ifndef VBOXNETADP
+ if (!vboxNetFltWinDoIncReferenceDevices(&pNetFlt->u.s.WinIf.MpState, &pNetFlt->u.s.WinIf.PtState, v))
+#else
+ if (!vboxNetFltWinDoIncReferenceDevice(&pNetFlt->u.s.WinIf.MpState, v))
+#endif
+ {
+ RTSpinlockRelease(pNetFlt->hSpinlock);
+ *pbNetFltActive = false;
+ return false;
+ }
+
+ if (pNetFlt->enmTrunkState != INTNETTRUNKIFSTATE_ACTIVE)
+ {
+ vboxNetFltWinIncReferenceModePassThru(pNetFlt, v);
+
+ RTSpinlockRelease((pNetFlt)->hSpinlock);
+ *pbNetFltActive = false;
+ return true;
+ }
+
+ vboxNetFltRetain(pNetFlt, true /* fBusy */);
+
+ vboxNetFltWinIncReferenceModeNetFlt(pNetFlt, v);
+
+ RTSpinlockRelease(pNetFlt->hSpinlock);
+
+ /* we have marked it as busy, so can do the res references outside the lock */
+ for (i = 0; i < v-1; i++)
+ {
+ vboxNetFltRetain(pNetFlt, true /* fBusy */);
+ }
+
+ *pbNetFltActive = true;
+
+ return true;
+}
+
+DECLINLINE(void) vboxNetFltWinDecReferenceNetFlt(PVBOXNETFLTINS pNetFlt, uint32_t n)
+{
+ uint32_t i;
+ for (i = 0; i < n; i++)
+ {
+ vboxNetFltRelease(pNetFlt, true);
+ }
+
+ vboxNetFltWinDecReferenceModeNetFlt(pNetFlt, n);
+}
+
+DECLINLINE(void) vboxNetFltWinDereferenceNetFlt(PVBOXNETFLTINS pNetFlt)
+{
+ vboxNetFltRelease(pNetFlt, true);
+
+ vboxNetFltWinDereferenceModeNetFlt(pNetFlt);
+}
+
+DECLINLINE(void) vboxNetFltWinDecReferenceWinIf(PVBOXNETFLTINS pNetFlt, uint32_t v)
+{
+#ifdef VBOXNETADP
+ vboxNetFltWinDecReferenceDevice(&pNetFlt->u.s.WinIf.MpState, v);
+#else
+ vboxNetFltWinDecReferenceDevices(&pNetFlt->u.s.WinIf.MpState, &pNetFlt->u.s.WinIf.PtState, v);
+#endif
+}
+
+DECLINLINE(void) vboxNetFltWinDereferenceWinIf(PVBOXNETFLTINS pNetFlt)
+{
+#ifdef VBOXNETADP
+ vboxNetFltWinDereferenceDevice(&pNetFlt->u.s.WinIf.MpState);
+#else
+ vboxNetFltWinDereferenceDevices(&pNetFlt->u.s.WinIf.MpState, &pNetFlt->u.s.WinIf.PtState);
+#endif
+}
+
+DECLINLINE(bool) vboxNetFltWinIncReferenceWinIf(PVBOXNETFLTINS pNetFlt, uint32_t v)
+{
+ Assert(v);
+ if (!v)
+ {
+ return false;
+ }
+
+ RTSpinlockAcquire(pNetFlt->hSpinlock);
+#ifdef VBOXNETADP
+ if (vboxNetFltWinDoIncReferenceDevice(&pNetFlt->u.s.WinIf.MpState, v))
+#else
+ if (vboxNetFltWinDoIncReferenceDevices(&pNetFlt->u.s.WinIf.MpState, &pNetFlt->u.s.WinIf.PtState, v))
+#endif
+ {
+ RTSpinlockRelease(pNetFlt->hSpinlock);
+ return true;
+ }
+
+ RTSpinlockRelease(pNetFlt->hSpinlock);
+ return false;
+}
+
+DECLINLINE(bool) vboxNetFltWinReferenceWinIf(PVBOXNETFLTINS pNetFlt)
+{
+ RTSpinlockAcquire(pNetFlt->hSpinlock);
+#ifdef VBOXNETADP
+ if (vboxNetFltWinDoReferenceDevice(&pNetFlt->u.s.WinIf.MpState))
+#else
+ if (vboxNetFltWinDoReferenceDevices(&pNetFlt->u.s.WinIf.MpState, &pNetFlt->u.s.WinIf.PtState))
+#endif
+ {
+ RTSpinlockRelease(pNetFlt->hSpinlock);
+ return true;
+ }
+
+ RTSpinlockRelease(pNetFlt->hSpinlock);
+ return false;
+}
+
+/***********************************************
+ * methods for accessing the network card info *
+ ***********************************************/
+
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinGetMacAddress(PVBOXNETFLTINS pNetFlt, PRTMAC pMac);
+DECLHIDDEN(bool) vboxNetFltWinIsPromiscuous(PVBOXNETFLTINS pNetFlt);
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinSetPromiscuous(PVBOXNETFLTINS pNetFlt, bool bYes);
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinQueryPhysicalMedium(PVBOXNETFLTINS pNetFlt, NDIS_PHYSICAL_MEDIUM * pMedium);
+
+/*********************
+ * mem alloc API *
+ *********************/
+
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinMemAlloc(PVOID* ppMemBuf, UINT cbLength);
+
+DECLHIDDEN(void) vboxNetFltWinMemFree(PVOID pMemBuf);
+
+/* convenience method used which allocates and initializes the PINTNETSG containing one
+ * segment referring the buffer of size cbBufSize
+ * the allocated PINTNETSG should be freed with the vboxNetFltWinMemFree.
+ *
+ * This is used when our ProtocolReceive callback is called and we have to return the indicated NDIS_PACKET
+ * on a callback exit. This is why we allocate the PINTNETSG and put the packet info there and enqueue it
+ * for the packet queue */
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinAllocSG(UINT cbBufSize, PINTNETSG *ppSG);
+
+/************************
+ * WinIf init/fini API *
+ ************************/
+#if defined(VBOXNETADP)
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinPtInitBind(PVBOXNETFLTINS *ppNetFlt, NDIS_HANDLE hMiniportAdapter, PNDIS_STRING pBindToMiniportName /* actually this is our miniport name*/, NDIS_HANDLE hWrapperConfigurationContext);
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinPtInitWinIf(PVBOXNETFLTWIN pWinIf);
+#else
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinPtInitBind(PVBOXNETFLTINS *ppNetFlt, PNDIS_STRING pOurMiniportName, PNDIS_STRING pBindToMiniportName);
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinPtInitWinIf(PVBOXNETFLTWIN pWinIf, PNDIS_STRING pOurDeviceName);
+#endif
+
+DECLHIDDEN(VOID) vboxNetFltWinPtFiniWinIf(PVBOXNETFLTWIN pWinIf);
+
+/************************************
+ * Execute Job at passive level API *
+ ************************************/
+
+typedef VOID (*PFNVBOXNETFLT_JOB_ROUTINE) (PVOID pContext);
+
+DECLHIDDEN(VOID) vboxNetFltWinJobSynchExecAtPassive(PFNVBOXNETFLT_JOB_ROUTINE pfnRoutine, PVOID pContext);
+
+/*******************************
+ * Ndis Packets processing API *
+ *******************************/
+DECLHIDDEN(PNDIS_PACKET) vboxNetFltWinNdisPacketFromSG(PVBOXNETFLTINS pNetFlt, PINTNETSG pSG, PVOID pBufToFree, bool bToWire, bool bCopyMemory);
+
+DECLHIDDEN(void) vboxNetFltWinFreeSGNdisPacket(PNDIS_PACKET pPacket, bool bFreeMem);
+
+#ifdef DEBUG_NETFLT_PACKETS
+#define DBG_CHECK_PACKETS(_p1, _p2) \
+ { \
+ bool _b = vboxNetFltWinMatchPackets(_p1, _p2, -1); \
+ Assert(_b); \
+ }
+
+#define DBG_CHECK_PACKET_AND_SG(_p, _sg) \
+ { \
+ bool _b = vboxNetFltWinMatchPacketAndSG(_p, _sg, -1); \
+ Assert(_b); \
+ }
+
+#define DBG_CHECK_SGS(_sg1, _sg2) \
+ { \
+ bool _b = vboxNetFltWinMatchSGs(_sg1, _sg2, -1); \
+ Assert(_b); \
+ }
+
+#else
+#define DBG_CHECK_PACKETS(_p1, _p2)
+#define DBG_CHECK_PACKET_AND_SG(_p, _sg)
+#define DBG_CHECK_SGS(_sg1, _sg2)
+#endif
+
+/**
+ * Ndis loops back broadcast packets posted to the wire by IntNet
+ * This routine is used in the mechanism of preventing this looping
+ *
+ * @param pAdapt
+ * @param pPacket
+ * @param bOnRecv true is we are receiving the packet from the wire
+ * false otherwise (i.e. the packet is from the host)
+ *
+ * @return true if the packet is a looped back one, false otherwise
+ */
+#ifdef VBOX_LOOPBACK_USEFLAGS
+DECLINLINE(bool) vboxNetFltWinIsLoopedBackPacket(PNDIS_PACKET pPacket)
+{
+ return (NdisGetPacketFlags(pPacket) & g_fPacketIsLoopedBack) == g_fPacketIsLoopedBack;
+}
+#endif
+
+/**************************************************************
+ * utility methods for ndis packet creation/initialization *
+ **************************************************************/
+
+#define VBOXNETFLT_OOB_INIT(_p) \
+ { \
+ NdisZeroMemory(NDIS_OOB_DATA_FROM_PACKET(_p), sizeof(NDIS_PACKET_OOB_DATA)); \
+ NDIS_SET_PACKET_HEADER_SIZE(_p, VBOXNETFLT_PACKET_ETHEADER_SIZE); \
+ }
+
+#ifndef VBOXNETADP
+
+DECLINLINE(NDIS_STATUS) vboxNetFltWinCopyPacketInfoOnRecv(PNDIS_PACKET pDstPacket, PNDIS_PACKET pSrcPacket, bool bForceStatusResources)
+{
+ NDIS_STATUS Status = bForceStatusResources ? NDIS_STATUS_RESOURCES : NDIS_GET_PACKET_STATUS(pSrcPacket);
+ NDIS_SET_PACKET_STATUS(pDstPacket, Status);
+
+ NDIS_PACKET_FIRST_NDIS_BUFFER(pDstPacket) = NDIS_PACKET_FIRST_NDIS_BUFFER(pSrcPacket);
+ NDIS_PACKET_LAST_NDIS_BUFFER(pDstPacket) = NDIS_PACKET_LAST_NDIS_BUFFER(pSrcPacket);
+
+ NdisGetPacketFlags(pDstPacket) = NdisGetPacketFlags(pSrcPacket);
+
+ NDIS_SET_ORIGINAL_PACKET(pDstPacket, NDIS_GET_ORIGINAL_PACKET(pSrcPacket));
+ NDIS_SET_PACKET_HEADER_SIZE(pDstPacket, NDIS_GET_PACKET_HEADER_SIZE(pSrcPacket));
+
+ return Status;
+}
+
+DECLINLINE(void) vboxNetFltWinCopyPacketInfoOnSend(PNDIS_PACKET pDstPacket, PNDIS_PACKET pSrcPacket)
+{
+ NDIS_PACKET_FIRST_NDIS_BUFFER(pDstPacket) = NDIS_PACKET_FIRST_NDIS_BUFFER(pSrcPacket);
+ NDIS_PACKET_LAST_NDIS_BUFFER(pDstPacket) = NDIS_PACKET_LAST_NDIS_BUFFER(pSrcPacket);
+
+ NdisGetPacketFlags(pDstPacket) = NdisGetPacketFlags(pSrcPacket);
+
+ NdisMoveMemory(NDIS_OOB_DATA_FROM_PACKET(pDstPacket),
+ NDIS_OOB_DATA_FROM_PACKET(pSrcPacket),
+ sizeof (NDIS_PACKET_OOB_DATA));
+
+ NdisIMCopySendPerPacketInfo(pDstPacket, pSrcPacket);
+
+ PVOID pMediaSpecificInfo = NULL;
+ UINT fMediaSpecificInfoSize = 0;
+
+ NDIS_GET_PACKET_MEDIA_SPECIFIC_INFO(pSrcPacket, &pMediaSpecificInfo, &fMediaSpecificInfoSize);
+
+ if (pMediaSpecificInfo || fMediaSpecificInfoSize)
+ {
+ NDIS_SET_PACKET_MEDIA_SPECIFIC_INFO(pDstPacket, pMediaSpecificInfo, fMediaSpecificInfoSize);
+ }
+}
+
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinPrepareSendPacket(PVBOXNETFLTINS pNetFlt, PNDIS_PACKET pPacket, PNDIS_PACKET *ppMyPacket);
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinPrepareRecvPacket(PVBOXNETFLTINS pNetFlt, PNDIS_PACKET pPacket, PNDIS_PACKET *ppMyPacket, bool bDpr);
+#endif
+
+DECLHIDDEN(void) vboxNetFltWinSleep(ULONG milis);
+
+#define MACS_EQUAL(_m1, _m2) \
+ ((_m1).au16[0] == (_m2).au16[0] \
+ && (_m1).au16[1] == (_m2).au16[1] \
+ && (_m1).au16[2] == (_m2).au16[2])
+
+
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinDetachFromInterface(PVBOXNETFLTINS pNetFlt, bool bOnUnbind);
+DECLHIDDEN(NDIS_STATUS) vboxNetFltWinCopyString(PNDIS_STRING pDst, PNDIS_STRING pSrc);
+
+
+/**
+ * Sets the enmState member atomically.
+ *
+ * Used for all updates.
+ *
+ * @param pThis The instance.
+ * @param enmNewState The new value.
+ */
+DECLINLINE(void) vboxNetFltWinSetWinIfState(PVBOXNETFLTINS pNetFlt, VBOXNETFLT_WINIFSTATE enmNewState)
+{
+ ASMAtomicWriteU32((uint32_t volatile *)&pNetFlt->u.s.WinIf.enmState, enmNewState);
+}
+
+/**
+ * Gets the enmState member atomically.
+ *
+ * Used for all reads.
+ *
+ * @returns The enmState value.
+ * @param pThis The instance.
+ */
+DECLINLINE(VBOXNETFLT_WINIFSTATE) vboxNetFltWinGetWinIfState(PVBOXNETFLTINS pNetFlt)
+{
+ return (VBOXNETFLT_WINIFSTATE)ASMAtomicUoReadU32((uint32_t volatile *)&pNetFlt->u.s.WinIf.enmState);
+}
+
+/* reference the driver module to prevent driver unload */
+DECLHIDDEN(void) vboxNetFltWinDrvReference();
+/* dereference the driver module to prevent driver unload */
+DECLHIDDEN(void) vboxNetFltWinDrvDereference();
+
+
+#ifndef VBOXNETADP
+# define VBOXNETFLT_PROMISCUOUS_SUPPORTED(_pNetFlt) (!(_pNetFlt)->fDisablePromiscuous)
+#else
+# define STATISTIC_INCREASE(_s) ASMAtomicIncU32((uint32_t volatile *)&(_s));
+
+DECLHIDDEN(void) vboxNetFltWinGenerateMACAddress(RTMAC *pMac);
+DECLHIDDEN(int) vboxNetFltWinMAC2NdisString(RTMAC *pMac, PNDIS_STRING pNdisString);
+DECLHIDDEN(int) vboxNetFltWinMACFromNdisString(RTMAC *pMac, PNDIS_STRING pNdisString);
+
+#endif
+#endif /* !VBOX_INCLUDED_SRC_VBoxNetFlt_win_drv_VBoxNetFltRt_win_h */
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetLwf-win.cpp b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetLwf-win.cpp
new file mode 100644
index 00000000..6520e909
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetLwf-win.cpp
@@ -0,0 +1,2736 @@
+/* $Id: VBoxNetLwf-win.cpp $ */
+/** @file
+ * VBoxNetLwf-win.cpp - NDIS6 Bridged 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 <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+#define LOG_GROUP LOG_GROUP_NET_FLT_DRV
+
+/*
+ * If VBOXNETLWF_SYNC_SEND is defined we won't allocate data buffers, but use
+ * the original buffers coming from IntNet to build MDLs around them. This
+ * also means that we need to wait for send operation to complete before
+ * returning the buffers, which hinders performance way too much.
+ */
+//#define VBOXNETLWF_SYNC_SEND
+
+/*
+ * If VBOXNETLWF_FIXED_SIZE_POOLS is defined we pre-allocate data buffers of
+ * fixed size in five pools. Each pool uses different size to accomodate packets
+ * of various sizes. We allocate these buffers once and re-use them when send
+ * operation is complete.
+ * If VBOXNETLWF_FIXED_SIZE_POOLS is not defined we allocate data buffers before
+ * each send operation and free then upon completion.
+ */
+#define VBOXNETLWF_FIXED_SIZE_POOLS
+
+/*
+ * Don't ask me why it is 42. Empirically this is what goes down the stack.
+ * OTOH, as we know from trustworthy sources, 42 is the answer, so be it.
+ */
+#define VBOXNETLWF_MAX_FRAME_SIZE(mtu) (mtu + 42)
+
+#include <VBox/version.h>
+#include <VBox/err.h>
+#include <iprt/initterm.h>
+#include <iprt/net.h>
+#include <iprt/list.h>
+#include <VBox/intnetinline.h>
+
+#include <iprt/nt/ntddk.h>
+#include <iprt/nt/ndis.h>
+#include <iprt/win/netioapi.h>
+#include <mstcpip.h>
+
+#define LogError(x) DbgPrint x
+
+#if 0
+#undef Log
+#define Log(x) DbgPrint x
+#undef LogFlow
+#define LogFlow(x) DbgPrint x
+#endif
+
+/** We have an entirely different structure than the one defined in VBoxNetFltCmn-win.h */
+typedef struct VBOXNETFLTWIN
+{
+ /** filter module context handle */
+ NDIS_HANDLE hModuleCtx;
+ /** IP address change notifier handle */
+ HANDLE hNotifier; /* Must be here as hModuleCtx may already be NULL when vboxNetFltOsDeleteInstance is called */
+} VBOXNETFLTWIN, *PVBOXNETFLTWIN;
+#define VBOXNETFLT_NO_PACKET_QUEUE
+#define VBOXNETFLT_OS_SPECFIC 1
+#include "VBoxNetFltInternal.h"
+
+#include "VBoxNetLwf-win.h"
+#include "VBox/VBoxNetCmn-win.h"
+
+typedef enum {
+ LwfState_Detached = 0,
+ LwfState_Attaching,
+ LwfState_Paused,
+ LwfState_Restarting,
+ LwfState_Running,
+ LwfState_Pausing,
+ LwfState_32BitHack = 0x7fffffff
+} VBOXNETLWFSTATE;
+
+/*
+ * 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 Stopping state.
+ */
+typedef enum {
+ LwfIdcState_Disconnected = 0, /* Initial state */
+ LwfIdcState_Connecting, /* Attemping to init IDC, worker thread running */
+ LwfIdcState_Connected, /* Successfully connected to IDC, worker thread terminated */
+ LwfIdcState_Stopping /* Terminating the worker thread and disconnecting IDC */
+} VBOXNETLWFIDCSTATE;
+
+struct _VBOXNETLWF_MODULE;
+
+typedef struct VBOXNETLWFGLOBALS
+{
+ /** synch event used for device creation synchronization */
+ //KEVENT SynchEvent;
+ /** Device reference count */
+ //int cDeviceRefs;
+ /** ndis device */
+ NDIS_HANDLE hDevice;
+ /** device object */
+ PDEVICE_OBJECT pDevObj;
+ /** our filter driver handle */
+ NDIS_HANDLE hFilterDriver;
+ /** lock protecting the module list */
+ NDIS_SPIN_LOCK Lock;
+ /** the head of module list */
+ RTLISTANCHOR listModules;
+ /** IDC initialization state */
+ volatile uint32_t enmIdcState;
+ /** IDC init thread handle */
+ HANDLE hInitIdcThread;
+} VBOXNETLWFGLOBALS, *PVBOXNETLWFGLOBALS;
+
+/**
+ * The (common) global data.
+ */
+static VBOXNETFLTGLOBALS g_VBoxNetFltGlobals;
+/* win-specific global data */
+VBOXNETLWFGLOBALS g_VBoxNetLwfGlobals;
+
+#ifdef VBOXNETLWF_FIXED_SIZE_POOLS
+static ULONG g_cbPool[] = { 576+56, 1556, 4096+56, 6192+56, 9056 };
+#endif /* VBOXNETLWF_FIXED_SIZE_POOLS */
+
+typedef struct _VBOXNETLWF_MODULE {
+ RTLISTNODE node;
+
+ NDIS_HANDLE hFilter;
+#ifndef VBOXNETLWF_FIXED_SIZE_POOLS
+ NDIS_HANDLE hPool;
+#else /* VBOXNETLWF_FIXED_SIZE_POOLS */
+ NDIS_HANDLE hPool[RT_ELEMENTS(g_cbPool)];
+#endif /* VBOXNETLWF_FIXED_SIZE_POOLS */
+ PVBOXNETLWFGLOBALS pGlobals;
+ /** Associated instance of NetFlt, one-to-one relationship */
+ PVBOXNETFLTINS pNetFlt; /// @todo Consider automic access!
+ /** Module state as described in http://msdn.microsoft.com/en-us/library/windows/hardware/ff550017(v=vs.85).aspx */
+ volatile uint32_t enmState; /* No lock needed yet, atomic should suffice. */
+ /** Mutex to prevent pausing while transmitting on behalf of NetFlt */
+ NDIS_MUTEX InTransmit;
+#ifdef VBOXNETLWF_SYNC_SEND
+ /** Event signalled when sending to the wire is complete */
+ KEVENT EventWire;
+ /** Event signalled when NDIS returns our receive notification */
+ KEVENT EventHost;
+#else /* !VBOXNETLWF_SYNC_SEND */
+ /** Event signalled when all pending sends (both to wire and host) have completed */
+ NDIS_EVENT EventSendComplete;
+ /** Counter for pending sends (both to wire and host) */
+ int32_t cPendingBuffers;
+ /** Work Item to deliver offloading indications at passive IRQL */
+ NDIS_HANDLE hWorkItem;
+#endif /* !VBOXNETLWF_SYNC_SEND */
+ /** MAC address of underlying adapter */
+ RTMAC MacAddr;
+ /** Size of offload config structure */
+ USHORT cbOffloadConfig;
+ /** Saved offload configuration */
+ PNDIS_OFFLOAD pSavedOffloadConfig;
+ /** Temporary buffer for disabling offload configuration */
+ PNDIS_OFFLOAD pDisabledOffloadConfig;
+ /** the cloned request we have passed down */
+ PNDIS_OID_REQUEST pPendingRequest;
+ /** true if the underlying miniport supplied offloading config */
+ bool fOffloadConfigValid;
+ /** true if the trunk expects data from us */
+ bool fActive;
+ /** true if the host wants the adapter to be in promisc mode */
+ bool fHostPromisc;
+ /** true if the user wants packets being sent or received by VMs to be visible to the host in promisc mode */
+ bool fPassVmTrafficToHost;
+ /** Name of underlying adapter */
+ char szMiniportName[1];
+} VBOXNETLWF_MODULE;
+typedef VBOXNETLWF_MODULE *PVBOXNETLWF_MODULE;
+
+/*
+ * A structure to wrap OID requests in.
+ */
+typedef struct _VBOXNETLWF_OIDREQ {
+ NDIS_OID_REQUEST Request;
+ NDIS_STATUS Status;
+ NDIS_EVENT Event;
+} VBOXNETLWF_OIDREQ;
+typedef VBOXNETLWF_OIDREQ *PVBOXNETLWF_OIDREQ;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static FILTER_ATTACH vboxNetLwfWinAttach;
+static FILTER_DETACH vboxNetLwfWinDetach;
+static FILTER_RESTART vboxNetLwfWinRestart;
+static FILTER_PAUSE vboxNetLwfWinPause;
+static FILTER_OID_REQUEST vboxNetLwfWinOidRequest;
+static FILTER_OID_REQUEST_COMPLETE vboxNetLwfWinOidRequestComplete;
+//static FILTER_CANCEL_OID_REQUEST vboxNetLwfWinCancelOidRequest;
+static FILTER_STATUS vboxNetLwfWinStatus;
+//static FILTER_NET_PNP_EVENT vboxNetLwfWinPnPEvent;
+static FILTER_SEND_NET_BUFFER_LISTS vboxNetLwfWinSendNetBufferLists;
+static FILTER_SEND_NET_BUFFER_LISTS_COMPLETE vboxNetLwfWinSendNetBufferListsComplete;
+static FILTER_RECEIVE_NET_BUFFER_LISTS vboxNetLwfWinReceiveNetBufferLists;
+static FILTER_RETURN_NET_BUFFER_LISTS vboxNetLwfWinReturnNetBufferLists;
+static KSTART_ROUTINE vboxNetLwfWinInitIdcWorker;
+
+static VOID vboxNetLwfWinUnloadDriver(IN PDRIVER_OBJECT pDriver);
+static int vboxNetLwfWinInitBase(void);
+static int vboxNetLwfWinFini(void);
+
+
+
+/**
+ * Logs an error to the system event log.
+ *
+ * @param ErrCode Error to report to event log.
+ * @param ReturnedStatus Error that was reported by the driver to the caller.
+ * @param uErrId Unique error id representing the location in the driver.
+ * @param cbDumpData Number of bytes at pDumpData.
+ * @param pDumpData Pointer to data that will be added to the message (see 'details' tab).
+ */
+static void vboxNetLwfLogErrorEvent(NTSTATUS uErrCode, NTSTATUS uReturnedStatus, ULONG uErrId)
+{
+ /* Figure out how many modules are attached and if they are going to fit into the dump data. */
+ unsigned cMaxModules = (ERROR_LOG_MAXIMUM_SIZE - FIELD_OFFSET(IO_ERROR_LOG_PACKET, DumpData)) / sizeof(RTMAC);
+ unsigned cModules = 0;
+ PVBOXNETLWF_MODULE pModuleCtx;
+ NdisAcquireSpinLock(&g_VBoxNetLwfGlobals.Lock);
+ RTListForEach(&g_VBoxNetLwfGlobals.listModules, pModuleCtx, VBOXNETLWF_MODULE, node)
+ ++cModules;
+ NdisReleaseSpinLock(&g_VBoxNetLwfGlobals.Lock);
+ /* Prevent overflow */
+ if (cModules > cMaxModules)
+ cModules = cMaxModules;
+
+ /* DumpDataSize must be a multiple of sizeof(ULONG). */
+ unsigned cbDumpData = (cModules * sizeof(RTMAC) + 3) & ~3;
+ /* Prevent underflow */
+ unsigned cbTotal = RT_MAX(FIELD_OFFSET(IO_ERROR_LOG_PACKET, DumpData) + cbDumpData,
+ sizeof(IO_ERROR_LOG_PACKET));
+
+ PIO_ERROR_LOG_PACKET pErrEntry;
+ pErrEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry(g_VBoxNetLwfGlobals.pDevObj,
+ (UCHAR)cbTotal);
+ if (pErrEntry)
+ {
+ PRTMAC pDump = (PRTMAC)pErrEntry->DumpData;
+ /*
+ * Initialize the whole structure with zeros in case we are suddenly short
+ * of data because the list is empty or has become smaller.
+ */
+ memset(pErrEntry, 0, cbTotal);
+
+ NdisAcquireSpinLock(&g_VBoxNetLwfGlobals.Lock);
+ RTListForEach(&g_VBoxNetLwfGlobals.listModules, pModuleCtx, VBOXNETLWF_MODULE, node)
+ {
+ /* The list could have been modified while we were allocating the entry, rely on cModules instead! */
+ if (cModules-- == 0)
+ break;
+ *pDump++ = pModuleCtx->MacAddr;
+ }
+ NdisReleaseSpinLock(&g_VBoxNetLwfGlobals.Lock);
+
+ pErrEntry->DumpDataSize = cbDumpData;
+ pErrEntry->ErrorCode = uErrCode;
+ pErrEntry->UniqueErrorValue = uErrId;
+ pErrEntry->FinalStatus = uReturnedStatus;
+ IoWriteErrorLogEntry(pErrEntry);
+ }
+ else
+ {
+ DbgPrint("Failed to allocate error log entry (cb=%u)\n", cbTotal);
+ }
+}
+
+#ifdef DEBUG
+
+static const char *vboxNetLwfWinStatusToText(NDIS_STATUS code)
+{
+ switch (code)
+ {
+ case NDIS_STATUS_MEDIA_CONNECT: return "NDIS_STATUS_MEDIA_CONNECT";
+ case NDIS_STATUS_MEDIA_DISCONNECT: return "NDIS_STATUS_MEDIA_DISCONNECT";
+ case NDIS_STATUS_RESET_START: return "NDIS_STATUS_RESET_START";
+ case NDIS_STATUS_RESET_END: return "NDIS_STATUS_RESET_END";
+ case NDIS_STATUS_MEDIA_BUSY: return "NDIS_STATUS_MEDIA_BUSY";
+ case NDIS_STATUS_MEDIA_SPECIFIC_INDICATION: return "NDIS_STATUS_MEDIA_SPECIFIC_INDICATION";
+ case NDIS_STATUS_LINK_SPEED_CHANGE: return "NDIS_STATUS_LINK_SPEED_CHANGE";
+ case NDIS_STATUS_LINK_STATE: return "NDIS_STATUS_LINK_STATE";
+ case NDIS_STATUS_PORT_STATE: return "NDIS_STATUS_PORT_STATE";
+ case NDIS_STATUS_OPER_STATUS: return "NDIS_STATUS_OPER_STATUS";
+ case NDIS_STATUS_NETWORK_CHANGE: return "NDIS_STATUS_NETWORK_CHANGE";
+ case NDIS_STATUS_PACKET_FILTER: return "NDIS_STATUS_PACKET_FILTER";
+ case NDIS_STATUS_TASK_OFFLOAD_CURRENT_CONFIG: return "NDIS_STATUS_TASK_OFFLOAD_CURRENT_CONFIG";
+ case NDIS_STATUS_TASK_OFFLOAD_HARDWARE_CAPABILITIES: return "NDIS_STATUS_TASK_OFFLOAD_HARDWARE_CAPABILITIES";
+ case NDIS_STATUS_OFFLOAD_ENCASPULATION_CHANGE: return "NDIS_STATUS_OFFLOAD_ENCASPULATION_CHANGE";
+ case NDIS_STATUS_TCP_CONNECTION_OFFLOAD_HARDWARE_CAPABILITIES: return "NDIS_STATUS_TCP_CONNECTION_OFFLOAD_HARDWARE_CAPABILITIES";
+ }
+ return "unknown";
+}
+
+static void vboxNetLwfWinDumpFilterTypes(ULONG uFlags)
+{
+ if (uFlags & NDIS_PACKET_TYPE_DIRECTED) Log5((" NDIS_PACKET_TYPE_DIRECTED\n"));
+ if (uFlags & NDIS_PACKET_TYPE_MULTICAST) Log5((" NDIS_PACKET_TYPE_MULTICAST\n"));
+ if (uFlags & NDIS_PACKET_TYPE_ALL_MULTICAST) Log5((" NDIS_PACKET_TYPE_ALL_MULTICAST\n"));
+ if (uFlags & NDIS_PACKET_TYPE_BROADCAST) Log5((" NDIS_PACKET_TYPE_BROADCAST\n"));
+ if (uFlags & NDIS_PACKET_TYPE_PROMISCUOUS) Log5((" NDIS_PACKET_TYPE_PROMISCUOUS\n"));
+ if (uFlags & NDIS_PACKET_TYPE_ALL_FUNCTIONAL) Log5((" NDIS_PACKET_TYPE_ALL_FUNCTIONAL\n"));
+ if (uFlags & NDIS_PACKET_TYPE_ALL_LOCAL) Log5((" NDIS_PACKET_TYPE_ALL_LOCAL\n"));
+ if (uFlags & NDIS_PACKET_TYPE_FUNCTIONAL) Log5((" NDIS_PACKET_TYPE_FUNCTIONAL\n"));
+ if (uFlags & NDIS_PACKET_TYPE_GROUP) Log5((" NDIS_PACKET_TYPE_GROUP\n"));
+ if (uFlags & NDIS_PACKET_TYPE_MAC_FRAME) Log5((" NDIS_PACKET_TYPE_MAC_FRAME\n"));
+ if (uFlags & NDIS_PACKET_TYPE_SMT) Log5((" NDIS_PACKET_TYPE_SMT\n"));
+ if (uFlags & NDIS_PACKET_TYPE_SOURCE_ROUTING) Log5((" NDIS_PACKET_TYPE_SOURCE_ROUTING\n"));
+ if (uFlags == 0) Log5((" NONE\n"));
+}
+
+DECLINLINE(void) vboxNetLwfWinDumpEncapsulation(const char *pcszText, ULONG uEncapsulation)
+{
+ if (uEncapsulation == NDIS_ENCAPSULATION_NOT_SUPPORTED)
+ Log5(("%s not supported\n", pcszText));
+ else
+ {
+ Log5(("%s", pcszText));
+ if (uEncapsulation & NDIS_ENCAPSULATION_NULL)
+ Log5((" null"));
+ if (uEncapsulation & NDIS_ENCAPSULATION_IEEE_802_3)
+ Log5((" 802.3"));
+ if (uEncapsulation & NDIS_ENCAPSULATION_IEEE_802_3_P_AND_Q)
+ Log5((" 802.3pq"));
+ if (uEncapsulation & NDIS_ENCAPSULATION_IEEE_802_3_P_AND_Q_IN_OOB)
+ Log5((" 802.3pq(oob)"));
+ if (uEncapsulation & NDIS_ENCAPSULATION_IEEE_LLC_SNAP_ROUTED)
+ Log5((" LLC"));
+ Log5(("\n"));
+ }
+}
+
+DECLINLINE(const char *) vboxNetLwfWinSetOnOffText(ULONG uOnOff)
+{
+ switch (uOnOff)
+ {
+ case NDIS_OFFLOAD_SET_NO_CHANGE: return "no change";
+ case NDIS_OFFLOAD_SET_ON: return "on";
+ case NDIS_OFFLOAD_SET_OFF: return "off";
+ }
+ return "unknown";
+}
+
+DECLINLINE(const char *) vboxNetLwfWinOnOffText(ULONG uOnOff)
+{
+ switch (uOnOff)
+ {
+ case NDIS_OFFLOAD_NOT_SUPPORTED: return "off";
+ case NDIS_OFFLOAD_SUPPORTED: return "on";
+ }
+ return "unknown";
+}
+
+DECLINLINE(const char *) vboxNetLwfWinSupportedText(ULONG uSupported)
+{
+ switch (uSupported)
+ {
+ case NDIS_OFFLOAD_NOT_SUPPORTED: return "not supported";
+ case NDIS_OFFLOAD_SUPPORTED: return "supported";
+ }
+ return "unknown";
+}
+
+static void vboxNetLwfWinDumpSetOffloadSettings(PNDIS_OFFLOAD pOffloadConfig)
+{
+ vboxNetLwfWinDumpEncapsulation(" Checksum.IPv4Transmit.Encapsulation =", pOffloadConfig->Checksum.IPv4Transmit.Encapsulation);
+ Log5((" Checksum.IPv4Transmit.IpOptionsSupported = %s\n", vboxNetLwfWinSetOnOffText(pOffloadConfig->Checksum.IPv4Transmit.IpOptionsSupported)));
+ Log5((" Checksum.IPv4Transmit.TcpOptionsSupported = %s\n", vboxNetLwfWinSetOnOffText(pOffloadConfig->Checksum.IPv4Transmit.TcpOptionsSupported)));
+ Log5((" Checksum.IPv4Transmit.TcpChecksum = %s\n", vboxNetLwfWinSetOnOffText(pOffloadConfig->Checksum.IPv4Transmit.TcpChecksum)));
+ Log5((" Checksum.IPv4Transmit.UdpChecksum = %s\n", vboxNetLwfWinSetOnOffText(pOffloadConfig->Checksum.IPv4Transmit.UdpChecksum)));
+ Log5((" Checksum.IPv4Transmit.IpChecksum = %s\n", vboxNetLwfWinSetOnOffText(pOffloadConfig->Checksum.IPv4Transmit.IpChecksum)));
+ vboxNetLwfWinDumpEncapsulation(" Checksum.IPv4Receive.Encapsulation =", pOffloadConfig->Checksum.IPv4Receive.Encapsulation);
+ Log5((" Checksum.IPv4Receive.IpOptionsSupported = %s\n", vboxNetLwfWinSetOnOffText(pOffloadConfig->Checksum.IPv4Receive.IpOptionsSupported)));
+ Log5((" Checksum.IPv4Receive.TcpOptionsSupported = %s\n", vboxNetLwfWinSetOnOffText(pOffloadConfig->Checksum.IPv4Receive.TcpOptionsSupported)));
+ Log5((" Checksum.IPv4Receive.TcpChecksum = %s\n", vboxNetLwfWinSetOnOffText(pOffloadConfig->Checksum.IPv4Receive.TcpChecksum)));
+ Log5((" Checksum.IPv4Receive.UdpChecksum = %s\n", vboxNetLwfWinSetOnOffText(pOffloadConfig->Checksum.IPv4Receive.UdpChecksum)));
+ Log5((" Checksum.IPv4Receive.IpChecksum = %s\n", vboxNetLwfWinSetOnOffText(pOffloadConfig->Checksum.IPv4Receive.IpChecksum)));
+ vboxNetLwfWinDumpEncapsulation(" Checksum.IPv6Transmit.Encapsulation =", pOffloadConfig->Checksum.IPv6Transmit.Encapsulation);
+ Log5((" Checksum.IPv6Transmit.IpExtensionHeadersSupported = %s\n", vboxNetLwfWinSetOnOffText(pOffloadConfig->Checksum.IPv6Transmit.IpExtensionHeadersSupported)));
+ Log5((" Checksum.IPv6Transmit.TcpOptionsSupported = %s\n", vboxNetLwfWinSetOnOffText(pOffloadConfig->Checksum.IPv6Transmit.TcpOptionsSupported)));
+ Log5((" Checksum.IPv6Transmit.TcpChecksum = %s\n", vboxNetLwfWinSetOnOffText(pOffloadConfig->Checksum.IPv6Transmit.TcpChecksum)));
+ Log5((" Checksum.IPv6Transmit.UdpChecksum = %s\n", vboxNetLwfWinSetOnOffText(pOffloadConfig->Checksum.IPv6Transmit.UdpChecksum)));
+ vboxNetLwfWinDumpEncapsulation(" Checksum.IPv6Receive.Encapsulation =", pOffloadConfig->Checksum.IPv6Receive.Encapsulation);
+ Log5((" Checksum.IPv6Receive.IpExtensionHeadersSupported = %s\n", vboxNetLwfWinSetOnOffText(pOffloadConfig->Checksum.IPv6Receive.IpExtensionHeadersSupported)));
+ Log5((" Checksum.IPv6Receive.TcpOptionsSupported = %s\n", vboxNetLwfWinSetOnOffText(pOffloadConfig->Checksum.IPv6Receive.TcpOptionsSupported)));
+ Log5((" Checksum.IPv6Receive.TcpChecksum = %s\n", vboxNetLwfWinSetOnOffText(pOffloadConfig->Checksum.IPv6Receive.TcpChecksum)));
+ Log5((" Checksum.IPv6Receive.UdpChecksum = %s\n", vboxNetLwfWinSetOnOffText(pOffloadConfig->Checksum.IPv6Receive.UdpChecksum)));
+ vboxNetLwfWinDumpEncapsulation(" LsoV1.IPv4.Encapsulation =", pOffloadConfig->LsoV1.IPv4.Encapsulation);
+ Log5((" LsoV1.IPv4.TcpOptions = %s\n", vboxNetLwfWinSupportedText(pOffloadConfig->LsoV1.IPv4.TcpOptions)));
+ Log5((" LsoV1.IPv4.IpOptions = %s\n", vboxNetLwfWinSupportedText(pOffloadConfig->LsoV1.IPv4.IpOptions)));
+ vboxNetLwfWinDumpEncapsulation(" LsoV2.IPv4.Encapsulation =", pOffloadConfig->LsoV2.IPv4.Encapsulation);
+ vboxNetLwfWinDumpEncapsulation(" LsoV2.IPv6.Encapsulation =", pOffloadConfig->LsoV2.IPv6.Encapsulation);
+ Log5((" LsoV2.IPv6.IpExtensionHeadersSupported = %s\n", vboxNetLwfWinSupportedText(pOffloadConfig->LsoV2.IPv6.IpExtensionHeadersSupported)));
+ Log5((" LsoV2.IPv6.TcpOptionsSupported = %s\n", vboxNetLwfWinSupportedText(pOffloadConfig->LsoV2.IPv6.TcpOptionsSupported)));
+}
+
+static void vboxNetLwfWinDumpOffloadSettings(PNDIS_OFFLOAD pOffloadConfig)
+{
+ vboxNetLwfWinDumpEncapsulation(" Checksum.IPv4Transmit.Encapsulation =", pOffloadConfig->Checksum.IPv4Transmit.Encapsulation);
+ Log5((" Checksum.IPv4Transmit.IpOptionsSupported = %s\n", vboxNetLwfWinOnOffText(pOffloadConfig->Checksum.IPv4Transmit.IpOptionsSupported)));
+ Log5((" Checksum.IPv4Transmit.TcpOptionsSupported = %s\n", vboxNetLwfWinOnOffText(pOffloadConfig->Checksum.IPv4Transmit.TcpOptionsSupported)));
+ Log5((" Checksum.IPv4Transmit.TcpChecksum = %s\n", vboxNetLwfWinOnOffText(pOffloadConfig->Checksum.IPv4Transmit.TcpChecksum)));
+ Log5((" Checksum.IPv4Transmit.UdpChecksum = %s\n", vboxNetLwfWinOnOffText(pOffloadConfig->Checksum.IPv4Transmit.UdpChecksum)));
+ Log5((" Checksum.IPv4Transmit.IpChecksum = %s\n", vboxNetLwfWinOnOffText(pOffloadConfig->Checksum.IPv4Transmit.IpChecksum)));
+ vboxNetLwfWinDumpEncapsulation(" Checksum.IPv4Receive.Encapsulation =", pOffloadConfig->Checksum.IPv4Receive.Encapsulation);
+ Log5((" Checksum.IPv4Receive.IpOptionsSupported = %s\n", vboxNetLwfWinOnOffText(pOffloadConfig->Checksum.IPv4Receive.IpOptionsSupported)));
+ Log5((" Checksum.IPv4Receive.TcpOptionsSupported = %s\n", vboxNetLwfWinOnOffText(pOffloadConfig->Checksum.IPv4Receive.TcpOptionsSupported)));
+ Log5((" Checksum.IPv4Receive.TcpChecksum = %s\n", vboxNetLwfWinOnOffText(pOffloadConfig->Checksum.IPv4Receive.TcpChecksum)));
+ Log5((" Checksum.IPv4Receive.UdpChecksum = %s\n", vboxNetLwfWinOnOffText(pOffloadConfig->Checksum.IPv4Receive.UdpChecksum)));
+ Log5((" Checksum.IPv4Receive.IpChecksum = %s\n", vboxNetLwfWinOnOffText(pOffloadConfig->Checksum.IPv4Receive.IpChecksum)));
+ vboxNetLwfWinDumpEncapsulation(" Checksum.IPv6Transmit.Encapsulation =", pOffloadConfig->Checksum.IPv6Transmit.Encapsulation);
+ Log5((" Checksum.IPv6Transmit.IpExtensionHeadersSupported = %s\n", vboxNetLwfWinOnOffText(pOffloadConfig->Checksum.IPv6Transmit.IpExtensionHeadersSupported)));
+ Log5((" Checksum.IPv6Transmit.TcpOptionsSupported = %s\n", vboxNetLwfWinOnOffText(pOffloadConfig->Checksum.IPv6Transmit.TcpOptionsSupported)));
+ Log5((" Checksum.IPv6Transmit.TcpChecksum = %s\n", vboxNetLwfWinOnOffText(pOffloadConfig->Checksum.IPv6Transmit.TcpChecksum)));
+ Log5((" Checksum.IPv6Transmit.UdpChecksum = %s\n", vboxNetLwfWinOnOffText(pOffloadConfig->Checksum.IPv6Transmit.UdpChecksum)));
+ vboxNetLwfWinDumpEncapsulation(" Checksum.IPv6Receive.Encapsulation =", pOffloadConfig->Checksum.IPv6Receive.Encapsulation);
+ Log5((" Checksum.IPv6Receive.IpExtensionHeadersSupported = %s\n", vboxNetLwfWinOnOffText(pOffloadConfig->Checksum.IPv6Receive.IpExtensionHeadersSupported)));
+ Log5((" Checksum.IPv6Receive.TcpOptionsSupported = %s\n", vboxNetLwfWinOnOffText(pOffloadConfig->Checksum.IPv6Receive.TcpOptionsSupported)));
+ Log5((" Checksum.IPv6Receive.TcpChecksum = %s\n", vboxNetLwfWinOnOffText(pOffloadConfig->Checksum.IPv6Receive.TcpChecksum)));
+ Log5((" Checksum.IPv6Receive.UdpChecksum = %s\n", vboxNetLwfWinOnOffText(pOffloadConfig->Checksum.IPv6Receive.UdpChecksum)));
+ vboxNetLwfWinDumpEncapsulation(" LsoV1.IPv4.Encapsulation =", pOffloadConfig->LsoV1.IPv4.Encapsulation);
+ Log5((" LsoV1.IPv4.TcpOptions = %s\n", vboxNetLwfWinSupportedText(pOffloadConfig->LsoV1.IPv4.TcpOptions)));
+ Log5((" LsoV1.IPv4.IpOptions = %s\n", vboxNetLwfWinSupportedText(pOffloadConfig->LsoV1.IPv4.IpOptions)));
+ vboxNetLwfWinDumpEncapsulation(" LsoV2.IPv4.Encapsulation =", pOffloadConfig->LsoV2.IPv4.Encapsulation);
+ vboxNetLwfWinDumpEncapsulation(" LsoV2.IPv6.Encapsulation =", pOffloadConfig->LsoV2.IPv6.Encapsulation);
+ Log5((" LsoV2.IPv6.IpExtensionHeadersSupported = %s\n", vboxNetLwfWinSupportedText(pOffloadConfig->LsoV2.IPv6.IpExtensionHeadersSupported)));
+ Log5((" LsoV2.IPv6.TcpOptionsSupported = %s\n", vboxNetLwfWinSupportedText(pOffloadConfig->LsoV2.IPv6.TcpOptionsSupported)));
+}
+
+static const char *vboxNetLwfWinStateToText(uint32_t enmState)
+{
+ switch (enmState)
+ {
+ case LwfState_Detached: return "Detached";
+ case LwfState_Attaching: return "Attaching";
+ case LwfState_Paused: return "Paused";
+ case LwfState_Restarting: return "Restarting";
+ case LwfState_Running: return "Running";
+ case LwfState_Pausing: return "Pausing";
+ }
+ return "invalid";
+}
+
+static void vboxNetLwfWinDumpPackets(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: src=%p cb=%d offset=%d", pszMsg, pList->SourceHandle, 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 *) vboxNetLwfWinEthTypeStr(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 VBOXNETLWF_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) vboxNetLwfWinDumpPacket(PCINTNETSG pSG, const char *cszText)
+{
+ uint8_t bPacket[VBOXNETLWF_PKTDMPSIZE];
+
+ uint32_t cb = pSG->cbTotal < VBOXNETLWF_PKTDMPSIZE ? pSG->cbTotal : VBOXNETLWF_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(("NetLWF: %s (%d bytes), %RTmac => %RTmac, EthType=%s(0x%x)\n",
+ cszText, pSG->cbTotal, pHdr+6, pHdr, vboxNetLwfWinEthTypeStr(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,
+ vboxNetLwfWinEthTypeStr(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): <not implemented>\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: <not implemented>\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: <not implemented>\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 vboxNetLwfWinDumpFilterTypes(uFlags) do { } while (0)
+# define vboxNetLwfWinDumpOffloadSettings(p) do { } while (0)
+# define vboxNetLwfWinDumpSetOffloadSettings(p) do { } while (0)
+# define vboxNetLwfWinDumpPackets(m,l) do { } while (0)
+# define vboxNetLwfWinDumpPacket(p,t) do { } while (0)
+#endif /* !DEBUG */
+
+DECLINLINE(bool) vboxNetLwfWinChangeState(PVBOXNETLWF_MODULE pModuleCtx, uint32_t enmNew, uint32_t enmOld = LwfState_32BitHack)
+{
+ AssertReturn(pModuleCtx, false);
+
+ bool fSuccess = true;
+ if (enmOld != LwfState_32BitHack)
+ {
+ fSuccess = ASMAtomicCmpXchgU32(&pModuleCtx->enmState, enmNew, enmOld);
+ if (fSuccess)
+ Log(("vboxNetLwfWinChangeState: state change %s -> %s\n",
+ vboxNetLwfWinStateToText(enmOld),
+ vboxNetLwfWinStateToText(enmNew)));
+ else
+ Log(("ERROR! vboxNetLwfWinChangeState: failed state change %s (actual=%s) -> %s\n",
+ vboxNetLwfWinStateToText(enmOld),
+ vboxNetLwfWinStateToText(ASMAtomicReadU32(&pModuleCtx->enmState)),
+ vboxNetLwfWinStateToText(enmNew)));
+ Assert(fSuccess);
+ }
+ else
+ {
+ uint32_t enmPrevState = ASMAtomicXchgU32(&pModuleCtx->enmState, enmNew);
+ Log(("vboxNetLwfWinChangeState: state change %s -> %s\n",
+ vboxNetLwfWinStateToText(enmPrevState),
+ vboxNetLwfWinStateToText(enmNew)));
+ NOREF(enmPrevState);
+ }
+ return fSuccess;
+}
+
+DECLINLINE(void) vboxNetLwfWinInitOidRequest(PVBOXNETLWF_OIDREQ pRequest)
+{
+ NdisZeroMemory(pRequest, sizeof(VBOXNETLWF_OIDREQ));
+
+ NdisInitializeEvent(&pRequest->Event);
+
+ pRequest->Request.Header.Type = NDIS_OBJECT_TYPE_OID_REQUEST;
+ pRequest->Request.Header.Revision = NDIS_OID_REQUEST_REVISION_1;
+ pRequest->Request.Header.Size = NDIS_SIZEOF_OID_REQUEST_REVISION_1;
+
+ pRequest->Request.RequestId = (PVOID)VBOXNETLWF_REQ_ID;
+}
+
+static NDIS_STATUS vboxNetLwfWinSyncOidRequest(PVBOXNETLWF_MODULE pModuleCtx, PVBOXNETLWF_OIDREQ pRequest)
+{
+ NDIS_STATUS Status = NdisFOidRequest(pModuleCtx->hFilter, &pRequest->Request);
+ if (Status == NDIS_STATUS_PENDING)
+ {
+ NdisWaitEvent(&pRequest->Event, 0);
+ Status = pRequest->Status;
+ }
+ return Status;
+}
+
+DECLINLINE(void) vboxNetLwfWinCopyOidRequestResults(PNDIS_OID_REQUEST pFrom, PNDIS_OID_REQUEST pTo)
+{
+ switch (pFrom->RequestType)
+ {
+ case NdisRequestSetInformation:
+ pTo->DATA.SET_INFORMATION.BytesRead = pFrom->DATA.SET_INFORMATION.BytesRead;
+ pTo->DATA.SET_INFORMATION.BytesNeeded = pFrom->DATA.SET_INFORMATION.BytesNeeded;
+ break;
+ case NdisRequestMethod:
+ pTo->DATA.METHOD_INFORMATION.OutputBufferLength = pFrom->DATA.METHOD_INFORMATION.OutputBufferLength;
+ pTo->DATA.METHOD_INFORMATION.BytesWritten = pFrom->DATA.METHOD_INFORMATION.BytesWritten;
+ pTo->DATA.METHOD_INFORMATION.BytesRead = pFrom->DATA.METHOD_INFORMATION.BytesRead;
+ pTo->DATA.METHOD_INFORMATION.BytesNeeded = pFrom->DATA.METHOD_INFORMATION.BytesNeeded;
+ break;
+ case NdisRequestQueryInformation:
+ case NdisRequestQueryStatistics:
+ default:
+ pTo->DATA.QUERY_INFORMATION.BytesWritten = pFrom->DATA.QUERY_INFORMATION.BytesWritten;
+ pTo->DATA.QUERY_INFORMATION.BytesNeeded = pFrom->DATA.QUERY_INFORMATION.BytesNeeded;
+ }
+}
+
+void inline vboxNetLwfWinOverridePacketFiltersUp(PVBOXNETLWF_MODULE pModuleCtx, ULONG *pFilters)
+{
+ if (ASMAtomicReadBool(&pModuleCtx->fActive) && !ASMAtomicReadBool(&pModuleCtx->fHostPromisc))
+ *pFilters &= ~NDIS_PACKET_TYPE_PROMISCUOUS;
+}
+
+NDIS_STATUS vboxNetLwfWinOidRequest(IN NDIS_HANDLE hModuleCtx,
+ IN PNDIS_OID_REQUEST pOidRequest)
+{
+ LogFlow(("==>vboxNetLwfWinOidRequest: module=%p\n", hModuleCtx));
+ vboxNetCmnWinDumpOidRequest(__FUNCTION__, pOidRequest);
+ PVBOXNETLWF_MODULE pModuleCtx = (PVBOXNETLWF_MODULE)hModuleCtx;
+ PNDIS_OID_REQUEST pClone = NULL;
+ NDIS_STATUS Status = NdisAllocateCloneOidRequest(pModuleCtx->hFilter,
+ pOidRequest,
+ VBOXNETLWF_MEM_TAG,
+ &pClone);
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ /* Save the pointer to the original */
+ *((PNDIS_OID_REQUEST*)(pClone->SourceReserved)) = pOidRequest;
+
+ pClone->RequestId = pOidRequest->RequestId;
+ /* We are not supposed to get another request until we are through with the one we "postponed" */
+ PNDIS_OID_REQUEST pPrev = ASMAtomicXchgPtrT(&pModuleCtx->pPendingRequest, pClone, PNDIS_OID_REQUEST);
+ Assert(pPrev == NULL);
+ pModuleCtx->pPendingRequest = pClone;
+ if (pOidRequest->RequestType == NdisRequestSetInformation
+ && pOidRequest->DATA.SET_INFORMATION.Oid == OID_GEN_CURRENT_PACKET_FILTER)
+ {
+ ASMAtomicWriteBool(&pModuleCtx->fHostPromisc, !!(*(ULONG*)pOidRequest->DATA.SET_INFORMATION.InformationBuffer & NDIS_PACKET_TYPE_PROMISCUOUS));
+ Log(("vboxNetLwfWinOidRequest: host wanted to set packet filter value to:\n"));
+ vboxNetLwfWinDumpFilterTypes(*(ULONG*)pOidRequest->DATA.SET_INFORMATION.InformationBuffer);
+ /* Keep adapter in promisc mode as long as we are active. */
+ if (ASMAtomicReadBool(&pModuleCtx->fActive))
+ *(ULONG*)pClone->DATA.SET_INFORMATION.InformationBuffer |= NDIS_PACKET_TYPE_PROMISCUOUS;
+ Log5(("vboxNetLwfWinOidRequest: pass the following packet filters to miniport:\n"));
+ vboxNetLwfWinDumpFilterTypes(*(ULONG*)pOidRequest->DATA.SET_INFORMATION.InformationBuffer);
+ }
+ if (pOidRequest->RequestType == NdisRequestSetInformation
+ && pOidRequest->DATA.SET_INFORMATION.Oid == OID_TCP_OFFLOAD_CURRENT_CONFIG)
+ {
+ Log5(("vboxNetLwfWinOidRequest: offloading set to:\n"));
+ vboxNetLwfWinDumpSetOffloadSettings((PNDIS_OFFLOAD)pOidRequest->DATA.SET_INFORMATION.InformationBuffer);
+ }
+
+ /* Forward the clone to underlying filters/miniport */
+ Status = NdisFOidRequest(pModuleCtx->hFilter, pClone);
+ if (Status != NDIS_STATUS_PENDING)
+ {
+ /* Synchronous completion */
+ pPrev = ASMAtomicXchgPtrT(&pModuleCtx->pPendingRequest, NULL, PNDIS_OID_REQUEST);
+ Assert(pPrev == pClone);
+ Log5(("vboxNetLwfWinOidRequest: got the following packet filters from miniport:\n"));
+ vboxNetLwfWinDumpFilterTypes(*(ULONG*)pOidRequest->DATA.QUERY_INFORMATION.InformationBuffer);
+ /*
+ * The host does not expect the adapter to be in promisc mode,
+ * unless it enabled the mode. Let's not disillusion it.
+ */
+ if ( pOidRequest->RequestType == NdisRequestQueryInformation
+ && pOidRequest->DATA.QUERY_INFORMATION.Oid == OID_GEN_CURRENT_PACKET_FILTER)
+ vboxNetLwfWinOverridePacketFiltersUp(pModuleCtx, (ULONG*)pOidRequest->DATA.QUERY_INFORMATION.InformationBuffer);
+ Log5(("vboxNetLwfWinOidRequest: reporting to the host the following packet filters:\n"));
+ vboxNetLwfWinDumpFilterTypes(*(ULONG*)pOidRequest->DATA.QUERY_INFORMATION.InformationBuffer);
+ vboxNetLwfWinCopyOidRequestResults(pClone, pOidRequest);
+ NdisFreeCloneOidRequest(pModuleCtx->hFilter, pClone);
+ }
+ /* In case of async completion we do the rest in vboxNetLwfWinOidRequestComplete() */
+ }
+ else
+ {
+ LogError(("vboxNetLwfWinOidRequest: NdisAllocateCloneOidRequest failed with 0x%x\n", Status));
+ }
+ LogFlow(("<==vboxNetLwfWinOidRequest: Status=0x%x\n", Status));
+ return Status;
+}
+
+VOID vboxNetLwfWinOidRequestComplete(IN NDIS_HANDLE hModuleCtx,
+ IN PNDIS_OID_REQUEST pRequest,
+ IN NDIS_STATUS Status)
+{
+ LogFlow(("==>vboxNetLwfWinOidRequestComplete: module=%p req=%p status=0x%x\n", hModuleCtx, pRequest, Status));
+ PVBOXNETLWF_MODULE pModuleCtx = (PVBOXNETLWF_MODULE)hModuleCtx;
+ PNDIS_OID_REQUEST pOriginal = *((PNDIS_OID_REQUEST*)(pRequest->SourceReserved));
+ if (pOriginal)
+ {
+ /* NDIS is supposed to serialize requests */
+ PNDIS_OID_REQUEST pPrev = ASMAtomicXchgPtrT(&pModuleCtx->pPendingRequest, NULL, PNDIS_OID_REQUEST);
+ Assert(pPrev == pRequest); NOREF(pPrev);
+
+ Log5(("vboxNetLwfWinOidRequestComplete: completed rq type=%d oid=%x\n", pRequest->RequestType, pRequest->DATA.QUERY_INFORMATION.Oid));
+ vboxNetLwfWinCopyOidRequestResults(pRequest, pOriginal);
+ if ( pRequest->RequestType == NdisRequestQueryInformation
+ && pRequest->DATA.QUERY_INFORMATION.Oid == OID_GEN_CURRENT_PACKET_FILTER)
+ {
+ Log5(("vboxNetLwfWinOidRequestComplete: underlying miniport reports its packet filters:\n"));
+ vboxNetLwfWinDumpFilterTypes(*(ULONG*)pRequest->DATA.QUERY_INFORMATION.InformationBuffer);
+ vboxNetLwfWinOverridePacketFiltersUp(pModuleCtx, (ULONG*)pRequest->DATA.QUERY_INFORMATION.InformationBuffer);
+ Log5(("vboxNetLwfWinOidRequestComplete: reporting the following packet filters to upper protocol:\n"));
+ vboxNetLwfWinDumpFilterTypes(*(ULONG*)pRequest->DATA.QUERY_INFORMATION.InformationBuffer);
+ }
+ NdisFreeCloneOidRequest(pModuleCtx->hFilter, pRequest);
+ NdisFOidRequestComplete(pModuleCtx->hFilter, pOriginal, Status);
+ }
+ else
+ {
+ /* This is not a clone, we originated it */
+ Log(("vboxNetLwfWinOidRequestComplete: locally originated request (%p) completed, status=0x%x\n", pRequest, Status));
+ PVBOXNETLWF_OIDREQ pRqWrapper = RT_FROM_MEMBER(pRequest, VBOXNETLWF_OIDREQ, Request);
+ pRqWrapper->Status = Status;
+ NdisSetEvent(&pRqWrapper->Event);
+ }
+ LogFlow(("<==vboxNetLwfWinOidRequestComplete\n"));
+}
+
+
+static bool vboxNetLwfWinIsPromiscuous(PVBOXNETLWF_MODULE pModuleCtx)
+{
+ return ASMAtomicReadBool(&pModuleCtx->fHostPromisc);
+}
+
+#if 0
+static NDIS_STATUS vboxNetLwfWinGetPacketFilter(PVBOXNETLWF_MODULE pModuleCtx)
+{
+ LogFlow(("==>vboxNetLwfWinGetPacketFilter: module=%p\n", pModuleCtx));
+ VBOXNETLWF_OIDREQ Rq;
+ vboxNetLwfWinInitOidRequest(&Rq);
+ Rq.Request.RequestType = NdisRequestQueryInformation;
+ Rq.Request.DATA.QUERY_INFORMATION.Oid = OID_GEN_CURRENT_PACKET_FILTER;
+ Rq.Request.DATA.QUERY_INFORMATION.InformationBuffer = &pModuleCtx->uPacketFilter;
+ Rq.Request.DATA.QUERY_INFORMATION.InformationBufferLength = sizeof(pModuleCtx->uPacketFilter);
+ NDIS_STATUS Status = vboxNetLwfWinSyncOidRequest(pModuleCtx, &Rq);
+ if (Status != NDIS_STATUS_SUCCESS)
+ {
+ LogError(("vboxNetLwfWinGetPacketFilter: vboxNetLwfWinSyncOidRequest(query, OID_GEN_CURRENT_PACKET_FILTER) failed with 0x%x\n", Status));
+ return FALSE;
+ }
+ if (Rq.Request.DATA.QUERY_INFORMATION.BytesWritten != sizeof(pModuleCtx->uPacketFilter))
+ {
+ LogError(("vboxNetLwfWinGetPacketFilter: vboxNetLwfWinSyncOidRequest(query, OID_GEN_CURRENT_PACKET_FILTER) failed to write neccessary amount (%d bytes), actually written %d bytes\n", sizeof(pModuleCtx->uPacketFilter), Rq.Request.DATA.QUERY_INFORMATION.BytesWritten));
+ }
+
+ Log5(("vboxNetLwfWinGetPacketFilter: OID_GEN_CURRENT_PACKET_FILTER query returned the following filters:\n"));
+ vboxNetLwfWinDumpFilterTypes(pModuleCtx->uPacketFilter);
+
+ LogFlow(("<==vboxNetLwfWinGetPacketFilter: status=0x%x\n", Status));
+ return Status;
+}
+#endif
+
+static NDIS_STATUS vboxNetLwfWinSetPacketFilter(PVBOXNETLWF_MODULE pModuleCtx, bool fPromisc)
+{
+ LogFlow(("==>vboxNetLwfWinSetPacketFilter: module=%p %s\n", pModuleCtx, fPromisc ? "promiscuous" : "normal"));
+ ULONG uFilter = 0;
+ VBOXNETLWF_OIDREQ Rq;
+ vboxNetLwfWinInitOidRequest(&Rq);
+ Rq.Request.RequestType = NdisRequestQueryInformation;
+ Rq.Request.DATA.QUERY_INFORMATION.Oid = OID_GEN_CURRENT_PACKET_FILTER;
+ Rq.Request.DATA.QUERY_INFORMATION.InformationBuffer = &uFilter;
+ Rq.Request.DATA.QUERY_INFORMATION.InformationBufferLength = sizeof(uFilter);
+ NDIS_STATUS Status = vboxNetLwfWinSyncOidRequest(pModuleCtx, &Rq);
+ if (Status != NDIS_STATUS_SUCCESS)
+ {
+ LogError(("vboxNetLwfWinSetPacketFilter: vboxNetLwfWinSyncOidRequest(query, OID_GEN_CURRENT_PACKET_FILTER) failed with 0x%x\n", Status));
+ return Status;
+ }
+ if (Rq.Request.DATA.QUERY_INFORMATION.BytesWritten != sizeof(uFilter))
+ {
+ LogError(("vboxNetLwfWinSetPacketFilter: vboxNetLwfWinSyncOidRequest(query, OID_GEN_CURRENT_PACKET_FILTER) failed to write neccessary amount (%d bytes), actually written %d bytes\n", sizeof(uFilter), Rq.Request.DATA.QUERY_INFORMATION.BytesWritten));
+ return NDIS_STATUS_FAILURE;
+ }
+
+ Log5(("vboxNetLwfWinSetPacketFilter: OID_GEN_CURRENT_PACKET_FILTER query returned the following filters:\n"));
+ vboxNetLwfWinDumpFilterTypes(uFilter);
+
+ if (fPromisc)
+ {
+ /* If we about to go promiscuous, save the state before we change it. */
+ ASMAtomicWriteBool(&pModuleCtx->fHostPromisc, !!(uFilter & NDIS_PACKET_TYPE_PROMISCUOUS));
+ uFilter |= NDIS_PACKET_TYPE_PROMISCUOUS;
+ }
+ else
+ {
+ /* Reset promisc only if it was not enabled before we had changed it. */
+ if (!ASMAtomicReadBool(&pModuleCtx->fHostPromisc))
+ uFilter &= ~NDIS_PACKET_TYPE_PROMISCUOUS;
+ }
+
+ Log5(("vboxNetLwfWinSetPacketFilter: OID_GEN_CURRENT_PACKET_FILTER about to set the following filters:\n"));
+ vboxNetLwfWinDumpFilterTypes(uFilter);
+
+ NdisResetEvent(&Rq.Event); /* need to reset as it has been set by query op */
+ Rq.Request.RequestType = NdisRequestSetInformation;
+ Rq.Request.DATA.SET_INFORMATION.Oid = OID_GEN_CURRENT_PACKET_FILTER;
+ Rq.Request.DATA.SET_INFORMATION.InformationBuffer = &uFilter;
+ Rq.Request.DATA.SET_INFORMATION.InformationBufferLength = sizeof(uFilter);
+ Status = vboxNetLwfWinSyncOidRequest(pModuleCtx, &Rq);
+ if (Status != NDIS_STATUS_SUCCESS)
+ {
+ LogError(("vboxNetLwfWinSetPacketFilter: vboxNetLwfWinSyncOidRequest(set, OID_GEN_CURRENT_PACKET_FILTER, vvv below vvv) failed with 0x%x\n", Status));
+ vboxNetLwfWinDumpFilterTypes(uFilter);
+ }
+ LogFlow(("<==vboxNetLwfWinSetPacketFilter: status=0x%x\n", Status));
+ return Status;
+}
+
+
+static NTSTATUS vboxNetLwfWinDevDispatch(IN PDEVICE_OBJECT pDevObj, IN PIRP pIrp)
+{
+ RT_NOREF1(pDevObj);
+ PIO_STACK_LOCATION pIrpSl = IoGetCurrentIrpStackLocation(pIrp);;
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ switch (pIrpSl->MajorFunction)
+ {
+ case IRP_MJ_DEVICE_CONTROL:
+ Status = STATUS_NOT_SUPPORTED;
+ break;
+ case IRP_MJ_CREATE:
+ case IRP_MJ_CLEANUP:
+ case IRP_MJ_CLOSE:
+ break;
+ default:
+ AssertFailed();
+ break;
+ }
+
+ pIrp->IoStatus.Status = Status;
+ IoCompleteRequest(pIrp, IO_NO_INCREMENT);
+
+ return Status;
+}
+
+/** @todo So far we had no use for device, should we even bother to create it? */
+static NDIS_STATUS vboxNetLwfWinDevCreate(PVBOXNETLWFGLOBALS pGlobals)
+{
+ NDIS_STRING DevName, LinkName;
+ PDRIVER_DISPATCH aMajorFunctions[IRP_MJ_MAXIMUM_FUNCTION+1];
+ NdisInitUnicodeString(&DevName, VBOXNETLWF_NAME_DEVICE);
+ NdisInitUnicodeString(&LinkName, VBOXNETLWF_NAME_LINK);
+
+ Assert(!pGlobals->hDevice);
+ Assert(!pGlobals->pDevObj);
+ NdisZeroMemory(aMajorFunctions, sizeof (aMajorFunctions));
+ aMajorFunctions[IRP_MJ_CREATE] = vboxNetLwfWinDevDispatch;
+ aMajorFunctions[IRP_MJ_CLEANUP] = vboxNetLwfWinDevDispatch;
+ aMajorFunctions[IRP_MJ_CLOSE] = vboxNetLwfWinDevDispatch;
+ aMajorFunctions[IRP_MJ_DEVICE_CONTROL] = vboxNetLwfWinDevDispatch;
+
+ NDIS_DEVICE_OBJECT_ATTRIBUTES DeviceAttributes;
+ NdisZeroMemory(&DeviceAttributes, sizeof(DeviceAttributes));
+ DeviceAttributes.Header.Type = NDIS_OBJECT_TYPE_DEVICE_OBJECT_ATTRIBUTES;
+ DeviceAttributes.Header.Revision = NDIS_DEVICE_OBJECT_ATTRIBUTES_REVISION_1;
+ DeviceAttributes.Header.Size = sizeof(DeviceAttributes);
+ DeviceAttributes.DeviceName = &DevName;
+ DeviceAttributes.SymbolicName = &LinkName;
+ DeviceAttributes.MajorFunctions = aMajorFunctions;
+ //DeviceAttributes.ExtensionSize = sizeof(FILTER_DEVICE_EXTENSION);
+
+ NDIS_STATUS Status = NdisRegisterDeviceEx(pGlobals->hFilterDriver,
+ &DeviceAttributes,
+ &pGlobals->pDevObj,
+ &pGlobals->hDevice);
+ Log(("vboxNetLwfWinDevCreate: NdisRegisterDeviceEx returned 0x%x\n", Status));
+ Assert(Status == NDIS_STATUS_SUCCESS);
+#if 0
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ PFILTER_DEVICE_EXTENSION pExtension;
+ pExtension = NdisGetDeviceReservedExtension(pGlobals->pDevObj);
+ pExtension->Signature = VBOXNETLWF_MEM_TAG;
+ pExtension->Handle = pGlobals->hFilterDriver;
+ }
+#endif
+ return Status;
+}
+
+static void vboxNetLwfWinDevDestroy(PVBOXNETLWFGLOBALS pGlobals)
+{
+ Assert(pGlobals->hDevice);
+ Assert(pGlobals->pDevObj);
+ NdisDeregisterDeviceEx(pGlobals->hDevice);
+ pGlobals->hDevice = NULL;
+ pGlobals->pDevObj = NULL;
+}
+
+static void vboxNetLwfWinDisableOffloading(PNDIS_OFFLOAD pOffloadConfig)
+{
+ pOffloadConfig->Checksum.IPv4Transmit.Encapsulation = NDIS_ENCAPSULATION_NOT_SUPPORTED;
+ pOffloadConfig->Checksum.IPv4Transmit.IpOptionsSupported = NDIS_OFFLOAD_NOT_SUPPORTED;
+ pOffloadConfig->Checksum.IPv4Transmit.TcpOptionsSupported = NDIS_OFFLOAD_NOT_SUPPORTED;
+ pOffloadConfig->Checksum.IPv4Transmit.TcpChecksum = NDIS_OFFLOAD_NOT_SUPPORTED;
+ pOffloadConfig->Checksum.IPv4Transmit.UdpChecksum = NDIS_OFFLOAD_NOT_SUPPORTED;
+ pOffloadConfig->Checksum.IPv4Transmit.IpChecksum = NDIS_OFFLOAD_NOT_SUPPORTED;
+ pOffloadConfig->Checksum.IPv6Transmit.Encapsulation = NDIS_ENCAPSULATION_NOT_SUPPORTED;
+ pOffloadConfig->Checksum.IPv6Transmit.IpExtensionHeadersSupported = NDIS_OFFLOAD_NOT_SUPPORTED;
+ pOffloadConfig->Checksum.IPv6Transmit.TcpOptionsSupported = NDIS_OFFLOAD_NOT_SUPPORTED;
+ pOffloadConfig->Checksum.IPv6Transmit.TcpChecksum = NDIS_OFFLOAD_NOT_SUPPORTED;
+ pOffloadConfig->Checksum.IPv6Transmit.UdpChecksum = NDIS_OFFLOAD_NOT_SUPPORTED;
+ pOffloadConfig->LsoV1.IPv4.Encapsulation = NDIS_ENCAPSULATION_NOT_SUPPORTED;
+ pOffloadConfig->LsoV1.IPv4.TcpOptions = NDIS_OFFLOAD_NOT_SUPPORTED;
+ pOffloadConfig->LsoV1.IPv4.IpOptions = NDIS_OFFLOAD_NOT_SUPPORTED;
+ pOffloadConfig->LsoV2.IPv4.Encapsulation = NDIS_ENCAPSULATION_NOT_SUPPORTED;
+ pOffloadConfig->LsoV2.IPv6.Encapsulation = NDIS_ENCAPSULATION_NOT_SUPPORTED;
+ pOffloadConfig->LsoV2.IPv6.IpExtensionHeadersSupported = NDIS_OFFLOAD_NOT_SUPPORTED;
+ pOffloadConfig->LsoV2.IPv6.TcpOptionsSupported = NDIS_OFFLOAD_NOT_SUPPORTED;
+}
+
+static void vboxNetLwfWinUpdateSavedOffloadConfig(PVBOXNETLWF_MODULE pModuleCtx, PNDIS_OFFLOAD pOffload)
+{
+ if (pModuleCtx->cbOffloadConfig < pOffload->Header.Size)
+ {
+ vboxNetLwfLogErrorEvent(IO_ERR_INTERNAL_ERROR, STATUS_SUCCESS, 10);
+ return;
+ }
+
+ NdisMoveMemory(pModuleCtx->pSavedOffloadConfig, pOffload, pOffload->Header.Size);
+ NdisMoveMemory(pModuleCtx->pDisabledOffloadConfig, pOffload, pOffload->Header.Size);
+ vboxNetLwfWinDisableOffloading(pModuleCtx->pDisabledOffloadConfig);
+ pModuleCtx->fOffloadConfigValid = true;
+}
+
+#ifdef VBOXNETLWF_FIXED_SIZE_POOLS
+static void vboxNetLwfWinFreePools(PVBOXNETLWF_MODULE pModuleCtx, int cPools)
+{
+ for (int i = 0; i < cPools; ++i)
+ {
+ if (pModuleCtx->hPool[i])
+ {
+ NdisFreeNetBufferListPool(pModuleCtx->hPool[i]);
+ Log4(("vboxNetLwfWinFreePools: freed NBL+NB pool 0x%p\n", pModuleCtx->hPool[i]));
+ }
+ }
+}
+#endif /* VBOXNETLWF_FIXED_SIZE_POOLS */
+
+
+static void vboxNetLwfWinFreeModuleResources(PVBOXNETLWF_MODULE pModuleCtx)
+{
+#ifdef VBOXNETLWF_FIXED_SIZE_POOLS
+ vboxNetLwfWinFreePools(pModuleCtx, RT_ELEMENTS(g_cbPool));
+#else /* !VBOXNETLWF_FIXED_SIZE_POOLS */
+ if (pModuleCtx->hPool)
+ {
+ NdisFreeNetBufferListPool(pModuleCtx->hPool);
+ Log4(("vboxNetLwfWinFreeModuleResources: freed NBL+NB pool 0x%p\n", pModuleCtx->hPool));
+ }
+#endif /* !VBOXNETLWF_FIXED_SIZE_POOLS */
+ if (pModuleCtx->pDisabledOffloadConfig)
+ NdisFreeMemory(pModuleCtx->pDisabledOffloadConfig, 0, 0);
+ if (pModuleCtx->pSavedOffloadConfig)
+ NdisFreeMemory(pModuleCtx->pSavedOffloadConfig, 0, 0);
+ if (pModuleCtx->hWorkItem)
+ NdisFreeIoWorkItem(pModuleCtx->hWorkItem);
+ NdisFreeMemory(pModuleCtx, 0, 0);
+}
+
+
+DECLARE_GLOBAL_CONST_UNICODE_STRING(g_strHostOnlyMiniportName, L"VirtualBox Host-Only");
+
+static NDIS_STATUS vboxNetLwfWinAttach(IN NDIS_HANDLE hFilter, IN NDIS_HANDLE hDriverCtx,
+ IN PNDIS_FILTER_ATTACH_PARAMETERS pParameters)
+{
+ LogFlow(("==>vboxNetLwfWinAttach: filter=%p\n", hFilter));
+
+ PVBOXNETLWFGLOBALS pGlobals = (PVBOXNETLWFGLOBALS)hDriverCtx;
+ if (!pGlobals)
+ {
+ vboxNetLwfLogErrorEvent(IO_ERR_INTERNAL_ERROR, NDIS_STATUS_FAILURE, 1);
+ return NDIS_STATUS_FAILURE;
+ }
+
+ /*
+ * We need a copy of NDIS_STRING structure as we are going to modify length
+ * of the base miniport instance name since RTL does not support comparing
+ * first n characters of two strings. We check if miniport names start with
+ * "Virtual Host-Only" to detect host-only adapters. It is a waste of resources
+ * to bind our filter to host-only adapters since they now operate independently.
+ */
+ NDIS_STRING strTruncatedInstanceName = *pParameters->BaseMiniportInstanceName; /* Do not copy data, only the structure itself */
+ strTruncatedInstanceName.Length = g_strHostOnlyMiniportName.Length; /* Truncate instance name */
+ if (RtlEqualUnicodeString(&strTruncatedInstanceName, &g_strHostOnlyMiniportName, TRUE /* Case insensitive */))
+ {
+ DbgPrint("vboxNetLwfWinAttach: won't attach to %wZ\n", pParameters->BaseMiniportInstanceName);
+ return NDIS_STATUS_FAILURE;
+ }
+
+ ANSI_STRING strMiniportName;
+ /* We use the miniport name to associate this filter module with the netflt instance */
+ NTSTATUS rc = RtlUnicodeStringToAnsiString(&strMiniportName,
+ pParameters->BaseMiniportName,
+ TRUE);
+ if (rc != STATUS_SUCCESS)
+ {
+ LogError(("vboxNetLwfWinAttach: RtlUnicodeStringToAnsiString(%ls) failed with 0x%x\n",
+ pParameters->BaseMiniportName, rc));
+ vboxNetLwfLogErrorEvent(IO_ERR_INTERNAL_ERROR, NDIS_STATUS_FAILURE, 2);
+ return NDIS_STATUS_FAILURE;
+ }
+ DbgPrint("vboxNetLwfWinAttach: friendly name=%wZ\n", pParameters->BaseMiniportInstanceName);
+ DbgPrint("vboxNetLwfWinAttach: name=%Z\n", &strMiniportName);
+
+ UINT cbModuleWithNameExtra = sizeof(VBOXNETLWF_MODULE) + strMiniportName.Length;
+ PVBOXNETLWF_MODULE pModuleCtx = (PVBOXNETLWF_MODULE)NdisAllocateMemoryWithTagPriority(hFilter,
+ cbModuleWithNameExtra,
+ VBOXNETLWF_MEM_TAG,
+ LowPoolPriority);
+ if (!pModuleCtx)
+ {
+ LogError(("vboxNetLwfWinAttach: Failed to allocate module context for %ls\n", pParameters->BaseMiniportName));
+ RtlFreeAnsiString(&strMiniportName);
+ vboxNetLwfLogErrorEvent(IO_ERR_INSUFFICIENT_RESOURCES, NDIS_STATUS_RESOURCES, 3);
+ return NDIS_STATUS_RESOURCES;
+ }
+ Log4(("vboxNetLwfWinAttach: allocated module context 0x%p\n", pModuleCtx));
+
+ NdisZeroMemory(pModuleCtx, cbModuleWithNameExtra);
+ NdisMoveMemory(pModuleCtx->szMiniportName, strMiniportName.Buffer, strMiniportName.Length);
+ RtlFreeAnsiString(&strMiniportName);
+
+ pModuleCtx->hWorkItem = NdisAllocateIoWorkItem(g_VBoxNetLwfGlobals.hFilterDriver);
+ if (!pModuleCtx->hWorkItem)
+ {
+ LogError(("vboxNetLwfWinAttach: Failed to allocate work item for %ls\n",
+ pParameters->BaseMiniportName));
+ NdisFreeMemory(pModuleCtx, 0, 0);
+ vboxNetLwfLogErrorEvent(IO_ERR_INSUFFICIENT_RESOURCES, NDIS_STATUS_RESOURCES, 4);
+ return NDIS_STATUS_RESOURCES;
+ }
+
+ Assert(pParameters->MacAddressLength == sizeof(RTMAC));
+ NdisMoveMemory(&pModuleCtx->MacAddr, pParameters->CurrentMacAddress, RT_MIN(sizeof(RTMAC), pParameters->MacAddressLength));
+
+ pModuleCtx->cbOffloadConfig = sizeof(NDIS_OFFLOAD) * 2; /* Best guess to accomodate future expansion. */
+ /* Get the exact size, if possible. */
+ if (pParameters->DefaultOffloadConfiguration)
+ pModuleCtx->cbOffloadConfig = pParameters->DefaultOffloadConfiguration->Header.Size;
+ else
+ vboxNetLwfLogErrorEvent(IO_ERR_INTERNAL_ERROR, STATUS_SUCCESS, 8);
+
+ pModuleCtx->pSavedOffloadConfig =
+ (PNDIS_OFFLOAD)NdisAllocateMemoryWithTagPriority(hFilter, pModuleCtx->cbOffloadConfig,
+ VBOXNETLWF_MEM_TAG, LowPoolPriority);
+ pModuleCtx->pDisabledOffloadConfig =
+ (PNDIS_OFFLOAD)NdisAllocateMemoryWithTagPriority(hFilter, pModuleCtx->cbOffloadConfig,
+ VBOXNETLWF_MEM_TAG, LowPoolPriority);
+ if (!pModuleCtx->pSavedOffloadConfig || !pModuleCtx->pDisabledOffloadConfig)
+ {
+ LogError(("vboxNetLwfWinAttach: Failed to allocate offload config buffers for %ls\n",
+ pParameters->BaseMiniportName));
+ vboxNetLwfWinFreeModuleResources(pModuleCtx);
+ vboxNetLwfLogErrorEvent(IO_ERR_INSUFFICIENT_RESOURCES, NDIS_STATUS_RESOURCES, 9);
+ return NDIS_STATUS_RESOURCES;
+ }
+
+ if (pParameters->DefaultOffloadConfiguration)
+ vboxNetLwfWinUpdateSavedOffloadConfig(pModuleCtx, pParameters->DefaultOffloadConfiguration);
+ else
+ {
+ NdisZeroMemory(pModuleCtx->pDisabledOffloadConfig, pModuleCtx->cbOffloadConfig);
+ pModuleCtx->pDisabledOffloadConfig->Header.Type = NDIS_OBJECT_TYPE_OFFLOAD;
+ pModuleCtx->pDisabledOffloadConfig->Header.Revision = NDIS_OFFLOAD_REVISION_1;
+ pModuleCtx->pDisabledOffloadConfig->Header.Size = NDIS_SIZEOF_NDIS_OFFLOAD_REVISION_1;
+ }
+
+ pModuleCtx->pGlobals = pGlobals;
+ pModuleCtx->hFilter = hFilter;
+ vboxNetLwfWinChangeState(pModuleCtx, LwfState_Attaching);
+ /* Initialize transmission mutex and events */
+ NDIS_INIT_MUTEX(&pModuleCtx->InTransmit);
+#ifdef VBOXNETLWF_SYNC_SEND
+ KeInitializeEvent(&pModuleCtx->EventWire, SynchronizationEvent, FALSE);
+ KeInitializeEvent(&pModuleCtx->EventHost, SynchronizationEvent, FALSE);
+#else /* !VBOXNETLWF_SYNC_SEND */
+ NdisInitializeEvent(&pModuleCtx->EventSendComplete);
+ pModuleCtx->cPendingBuffers = 0;
+#endif /* !VBOXNETLWF_SYNC_SEND */
+
+#ifdef VBOXNETLWF_FIXED_SIZE_POOLS
+ for (int i = 0; i < RT_ELEMENTS(g_cbPool); ++i)
+ {
+ /* Allocate buffer pools */
+ 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; /** @todo Do we need to consider underlying drivers? I think not. */
+ PoolParams.PoolTag = VBOXNETLWF_MEM_TAG;
+ PoolParams.DataSize = g_cbPool[i];
+ pModuleCtx->hPool[i] = NdisAllocateNetBufferListPool(hFilter, &PoolParams);
+ if (!pModuleCtx->hPool[i])
+ {
+ LogError(("vboxNetLwfWinAttach: NdisAllocateNetBufferListPool failed\n"));
+ vboxNetLwfWinFreeModuleResources(pModuleCtx);
+ vboxNetLwfLogErrorEvent(IO_ERR_INSUFFICIENT_RESOURCES, NDIS_STATUS_RESOURCES, 7);
+ return NDIS_STATUS_RESOURCES;
+ }
+ Log4(("vboxNetLwfWinAttach: allocated NBL+NB pool (data size=%u) 0x%p\n",
+ PoolParams.DataSize, pModuleCtx->hPool[i]));
+ }
+#else /* !VBOXNETLWF_FIXED_SIZE_POOLS */
+ /* Allocate buffer pools */
+ 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; /** @todo Do we need to consider underlying drivers? I think not. */
+ PoolParams.PoolTag = VBOXNETLWF_MEM_TAG;
+ pModuleCtx->hPool = NdisAllocateNetBufferListPool(hFilter, &PoolParams);
+ if (!pModuleCtx->hPool)
+ {
+ LogError(("vboxNetLwfWinAttach: NdisAllocateNetBufferListPool failed\n"));
+ vboxNetLwfWinFreeModuleResources(pModuleCtx);
+ vboxNetLwfLogErrorEvent(IO_ERR_INSUFFICIENT_RESOURCES, NDIS_STATUS_RESOURCES, 7);
+ return NDIS_STATUS_RESOURCES;
+ }
+ Log4(("vboxNetLwfWinAttach: allocated NBL+NB pool 0x%p\n", pModuleCtx->hPool));
+#endif /* !VBOXNETLWF_FIXED_SIZE_POOLS */
+
+ NDIS_FILTER_ATTRIBUTES Attributes;
+ NdisZeroMemory(&Attributes, sizeof(Attributes));
+ Attributes.Header.Revision = NDIS_FILTER_ATTRIBUTES_REVISION_1;
+ Attributes.Header.Size = sizeof(Attributes);
+ Attributes.Header.Type = NDIS_OBJECT_TYPE_FILTER_ATTRIBUTES;
+ Attributes.Flags = 0;
+ NDIS_STATUS Status = NdisFSetAttributes(hFilter, pModuleCtx, &Attributes);
+ if (Status != NDIS_STATUS_SUCCESS)
+ {
+ LogError(("vboxNetLwfWinAttach: NdisFSetAttributes failed with 0x%x\n", Status));
+ vboxNetLwfWinFreeModuleResources(pModuleCtx);
+ vboxNetLwfLogErrorEvent(IO_ERR_INTERNAL_ERROR, NDIS_STATUS_RESOURCES, 5);
+ return NDIS_STATUS_RESOURCES;
+ }
+ /* Insert into module chain */
+ NdisAcquireSpinLock(&pGlobals->Lock);
+ RTListPrepend(&pGlobals->listModules, &pModuleCtx->node);
+ NdisReleaseSpinLock(&pGlobals->Lock);
+
+ vboxNetLwfWinChangeState(pModuleCtx, LwfState_Paused);
+
+ /// @todo Somehow the packet filter is 0 at this point: Status = vboxNetLwfWinGetPacketFilter(pModuleCtx);
+ /// @todo We actually update it later in status handler, perhaps we should not do anything here.
+
+ LogFlow(("<==vboxNetLwfWinAttach: Status = 0x%x\n", Status));
+ return Status;
+}
+
+static VOID vboxNetLwfWinDetach(IN NDIS_HANDLE hModuleCtx)
+{
+ LogFlow(("==>vboxNetLwfWinDetach: module=%p\n", hModuleCtx));
+ PVBOXNETLWF_MODULE pModuleCtx = (PVBOXNETLWF_MODULE)hModuleCtx;
+ vboxNetLwfWinChangeState(pModuleCtx, LwfState_Detached, LwfState_Paused);
+
+ /* Remove from module chain */
+ NdisAcquireSpinLock(&pModuleCtx->pGlobals->Lock);
+ RTListNodeRemove(&pModuleCtx->node);
+ NdisReleaseSpinLock(&pModuleCtx->pGlobals->Lock);
+
+ PVBOXNETFLTINS pNetFltIns = pModuleCtx->pNetFlt; /// @todo Atomic?
+ if (pNetFltIns && vboxNetFltTryRetainBusyNotDisconnected(pNetFltIns))
+ {
+ /*
+ * Set hModuleCtx to null now in order to prevent filter restart,
+ * OID requests and other stuff associated with NetFlt deactivation.
+ */
+ pNetFltIns->u.s.WinIf.hModuleCtx = NULL;
+ /* Notify NetFlt that we are going down */
+ pNetFltIns->pSwitchPort->pfnDisconnect(pNetFltIns->pSwitchPort, &pNetFltIns->MyPort, vboxNetFltPortReleaseBusy);
+ /* We do not 'release' netflt instance since it has been done by pfnDisconnect */
+ }
+ pModuleCtx->pNetFlt = NULL;
+
+ /*
+ * We have to make sure that all NET_BUFFER_LIST structures have been freed by now, but
+ * it does not require us to do anything here since it has already been taken care of
+ * by vboxNetLwfWinPause().
+ */
+ vboxNetLwfWinFreeModuleResources(pModuleCtx);
+ Log4(("vboxNetLwfWinDetach: freed module context 0x%p\n", pModuleCtx));
+ LogFlow(("<==vboxNetLwfWinDetach\n"));
+}
+
+
+static NDIS_STATUS vboxNetLwfWinPause(IN NDIS_HANDLE hModuleCtx, IN PNDIS_FILTER_PAUSE_PARAMETERS pParameters)
+{
+ RT_NOREF1(pParameters);
+ LogFlow(("==>vboxNetLwfWinPause: module=%p\n", hModuleCtx));
+ PVBOXNETLWF_MODULE pModuleCtx = (PVBOXNETLWF_MODULE)hModuleCtx;
+ vboxNetLwfWinChangeState(pModuleCtx, LwfState_Pausing, LwfState_Running);
+ /* Wait for pending send/indication operations to complete. */
+ NDIS_WAIT_FOR_MUTEX(&pModuleCtx->InTransmit);
+#ifndef VBOXNETLWF_SYNC_SEND
+ NdisWaitEvent(&pModuleCtx->EventSendComplete, 1000 /* ms */);
+#endif /* !VBOXNETLWF_SYNC_SEND */
+ vboxNetLwfWinChangeState(pModuleCtx, LwfState_Paused, LwfState_Pausing);
+ NDIS_RELEASE_MUTEX(&pModuleCtx->InTransmit);
+ LogFlow(("<==vboxNetLwfWinPause\n"));
+ return NDIS_STATUS_SUCCESS; /* Failure is not an option */
+}
+
+
+static void vboxNetLwfWinIndicateOffload(PVBOXNETLWF_MODULE pModuleCtx, PNDIS_OFFLOAD pOffload)
+{
+ Log5(("vboxNetLwfWinIndicateOffload: offload config changed to:\n"));
+ vboxNetLwfWinDumpOffloadSettings(pOffload);
+ NDIS_STATUS_INDICATION OffloadingIndication;
+ NdisZeroMemory(&OffloadingIndication, sizeof(OffloadingIndication));
+ OffloadingIndication.Header.Type = NDIS_OBJECT_TYPE_STATUS_INDICATION;
+ OffloadingIndication.Header.Revision = NDIS_STATUS_INDICATION_REVISION_1;
+ OffloadingIndication.Header.Size = NDIS_SIZEOF_STATUS_INDICATION_REVISION_1;
+ OffloadingIndication.SourceHandle = pModuleCtx->hFilter;
+ OffloadingIndication.StatusCode = NDIS_STATUS_TASK_OFFLOAD_CURRENT_CONFIG;
+ OffloadingIndication.StatusBuffer = pOffload;
+ OffloadingIndication.StatusBufferSize = pOffload->Header.Size;
+ NdisFIndicateStatus(pModuleCtx->hFilter, &OffloadingIndication);
+}
+
+
+static NDIS_STATUS vboxNetLwfWinRestart(IN NDIS_HANDLE hModuleCtx, IN PNDIS_FILTER_RESTART_PARAMETERS pParameters)
+{
+ RT_NOREF1(pParameters);
+ LogFlow(("==>vboxNetLwfWinRestart: module=%p\n", hModuleCtx));
+ PVBOXNETLWF_MODULE pModuleCtx = (PVBOXNETLWF_MODULE)hModuleCtx;
+ vboxNetLwfWinChangeState(pModuleCtx, LwfState_Restarting, LwfState_Paused);
+
+ /* By default the packets that go between VMs and wire are invisible to the host. */
+ pModuleCtx->fPassVmTrafficToHost = false;
+
+ NDIS_HANDLE hConfig;
+ NDIS_CONFIGURATION_OBJECT cfgObj;
+ cfgObj.Header.Type = NDIS_OBJECT_TYPE_CONFIGURATION_OBJECT;
+ cfgObj.Header.Revision = NDIS_CONFIGURATION_OBJECT_REVISION_1;
+ cfgObj.Header.Size = sizeof(NDIS_CONFIGURATION_OBJECT);
+ cfgObj.NdisHandle = g_VBoxNetLwfGlobals.hFilterDriver;
+
+ NDIS_STATUS Status = NdisOpenConfigurationEx(&cfgObj, &hConfig);
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ NDIS_STRING strCfgParam = NDIS_STRING_CONST("PassVmTrafficToHost");
+ PNDIS_CONFIGURATION_PARAMETER pParam = NULL;
+ NdisReadConfiguration(&Status, &pParam, hConfig, &strCfgParam, NdisParameterInteger);
+ if (Status != NDIS_STATUS_SUCCESS)
+ {
+ Log(("vboxNetLwfWinRestart: Failed to read 'PassVmTrafficToHost' from the registry.\n"));
+ }
+ else if (pParam->ParameterData.IntegerData != 0)
+ {
+ Log(("vboxNetLwfWinRestart: Allowing the host to see VM traffic in promisc mode by user request.\n"));
+ pModuleCtx->fPassVmTrafficToHost = true;
+ }
+ NdisCloseConfiguration(hConfig);
+ }
+ vboxNetLwfWinChangeState(pModuleCtx, LwfState_Running, LwfState_Restarting);
+ LogFlow(("<==vboxNetLwfWinRestart: Status = 0x%x, returning NDIS_STATUS_SUCCESS nontheless.\n", Status));
+ return NDIS_STATUS_SUCCESS;
+}
+
+
+static void vboxNetLwfWinDestroySG(PINTNETSG pSG)
+{
+ NdisFreeMemory(pSG, 0, 0);
+ Log4(("vboxNetLwfWinDestroySG: freed SG 0x%p\n", pSG));
+}
+
+/**
+ * Worker for vboxNetLwfWinNBtoSG() that gets the max segment count needed.
+ * @note vboxNetLwfWinNBtoSG may use fewer depending on cbPacket and offset!
+ * @note vboxNetAdpWinCalcSegments() is a copy of this code.
+ */
+DECLINLINE(ULONG) vboxNetLwfWinCalcSegments(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;
+}
+
+DECLINLINE(void) vboxNetLwfWinFreeMdlChain(PMDL pMdl)
+{
+#ifndef VBOXNETLWF_FIXED_SIZE_POOLS
+ PMDL pMdlNext;
+ while (pMdl)
+ {
+ pMdlNext = pMdl->Next;
+# ifndef VBOXNETLWF_SYNC_SEND
+ PUCHAR pDataBuf;
+ ULONG cb = 0;
+ NdisQueryMdl(pMdl, &pDataBuf, &cb, NormalPagePriority);
+# endif /* !VBOXNETLWF_SYNC_SEND */
+ NdisFreeMdl(pMdl);
+ Log4(("vboxNetLwfWinFreeMdlChain: freed MDL 0x%p\n", pMdl));
+# ifndef VBOXNETLWF_SYNC_SEND
+ NdisFreeMemory(pDataBuf, 0, 0);
+ Log4(("vboxNetLwfWinFreeMdlChain: freed data buffer 0x%p\n", pDataBuf));
+# endif /* !VBOXNETLWF_SYNC_SEND */
+ pMdl = pMdlNext;
+ }
+#else /* VBOXNETLWF_FIXED_SIZE_POOLS */
+ RT_NOREF1(pMdl);
+#endif /* VBOXNETLWF_FIXED_SIZE_POOLS */
+}
+
+/** @todo
+ * 1) Copy data from SG to MDL (if we decide to complete asynchronously).
+ * 2) Provide context/backfill space. Nobody does it, should we?
+ * 3) We always get a single segment from intnet. Simplify?
+ */
+static PNET_BUFFER_LIST vboxNetLwfWinSGtoNB(PVBOXNETLWF_MODULE pModule, PINTNETSG pSG)
+{
+ AssertReturn(pSG->cSegsUsed >= 1, NULL);
+ LogFlow(("==>vboxNetLwfWinSGtoNB: segments=%d hPool=%p cb=%u\n", pSG->cSegsUsed,
+ pModule->hPool, pSG->cbTotal));
+ AssertReturn(pModule->hPool, NULL);
+
+#ifdef VBOXNETLWF_SYNC_SEND
+ PINTNETSEG pSeg = pSG->aSegs;
+ PMDL pMdl = NdisAllocateMdl(pModule->hFilter, pSeg->pv, pSeg->cb);
+ if (!pMdl)
+ {
+ LogError(("vboxNetLwfWinSGtoNB: failed to allocate an MDL\n"));
+ LogFlow(("<==vboxNetLwfWinSGtoNB: return NULL\n"));
+ return NULL;
+ }
+ Log4(("vboxNetLwfWinSGtoNB: allocated Mdl 0x%p\n", pMdl));
+ PMDL pMdlCurr = pMdl;
+ for (int i = 1; i < pSG->cSegsUsed; i++)
+ {
+ pSeg = &pSG->aSegs[i];
+ pMdlCurr->Next = NdisAllocateMdl(pModule->hFilter, pSeg->pv, pSeg->cb);
+ if (!pMdlCurr->Next)
+ {
+ LogError(("vboxNetLwfWinSGtoNB: failed to allocate an MDL\n"));
+ /* Tear down all MDL we chained so far */
+ vboxNetLwfWinFreeMdlChain(pMdl);
+ return NULL;
+ }
+ pMdlCurr = pMdlCurr->Next;
+ Log4(("vboxNetLwfWinSGtoNB: allocated Mdl 0x%p\n", pMdlCurr));
+ }
+ PNET_BUFFER_LIST pBufList = NdisAllocateNetBufferAndNetBufferList(pModule->hPool,
+ 0 /* ContextSize */,
+ 0 /* ContextBackFill */,
+ pMdl,
+ 0 /* DataOffset */,
+ pSG->cbTotal);
+ if (pBufList)
+ {
+ Log4(("vboxNetLwfWinSGtoNB: allocated NBL+NB 0x%p\n", pBufList));
+ pBufList->SourceHandle = pModule->hFilter;
+ /** @todo Do we need to initialize anything else? */
+ }
+ else
+ {
+ LogError(("vboxNetLwfWinSGtoNB: failed to allocate an NBL+NB\n"));
+ vboxNetLwfWinFreeMdlChain(pMdl);
+ }
+#else /* !VBOXNETLWF_SYNC_SEND */
+
+# ifdef VBOXNETLWF_FIXED_SIZE_POOLS
+ int iPool = 0;
+ ULONG cbFrame = VBOXNETLWF_MAX_FRAME_SIZE(pSG->cbTotal);
+ /* Let's find the appropriate pool first */
+ for (iPool = 0; iPool < RT_ELEMENTS(g_cbPool); ++iPool)
+ if (cbFrame <= g_cbPool[iPool])
+ break;
+ if (iPool >= RT_ELEMENTS(g_cbPool))
+ {
+ LogError(("vboxNetLwfWinSGtoNB: frame is too big (%u > %u), drop it.\n", cbFrame, g_cbPool[RT_ELEMENTS(g_cbPool)-1]));
+ LogFlow(("<==vboxNetLwfWinSGtoNB: return NULL\n"));
+ return NULL;
+ }
+ PNET_BUFFER_LIST pBufList = NdisAllocateNetBufferList(pModule->hPool[iPool],
+ 0 /** @todo ContextSize */,
+ 0 /** @todo ContextBackFill */);
+ if (!pBufList)
+ {
+ LogError(("vboxNetLwfWinSGtoNB: failed to allocate netbuffer (cb=%u) from pool %d\n", cbFrame, iPool));
+ LogFlow(("<==vboxNetLwfWinSGtoNB: return NULL\n"));
+ return NULL;
+ }
+ const ULONG cbAlignmentMask = sizeof(USHORT) - 1; /* Microsoft LB/FO provider expects packets to be aligned at word boundary. */
+ ULONG cbAlignedFrame = (pSG->cbTotal + cbAlignmentMask) & ~cbAlignmentMask;
+ Assert(cbAlignedFrame >= pSG->cbTotal);
+ Assert(cbFrame >= cbAlignedFrame);
+ NET_BUFFER *pBuffer = NET_BUFFER_LIST_FIRST_NB(pBufList);
+ NDIS_STATUS Status = NdisRetreatNetBufferDataStart(pBuffer, cbAlignedFrame, 0 /** @todo DataBackfill */, NULL);
+ if (cbAlignedFrame - pSG->cbTotal > 0)
+ {
+ /* Make sure padding zeros do not get to the wire. */
+ if (NET_BUFFER_DATA_LENGTH(pBuffer) != cbAlignedFrame)
+ vboxNetLwfLogErrorEvent(IO_ERR_INTERNAL_ERROR, STATUS_SUCCESS, 11);
+ else
+ NET_BUFFER_DATA_LENGTH(pBuffer) = pSG->cbTotal;
+ }
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ uint8_t *pDst = (uint8_t*)NdisGetDataBuffer(pBuffer, pSG->cbTotal, NULL, 1, 0);
+ if (pDst)
+ {
+ for (int i = 0; i < pSG->cSegsUsed; i++)
+ {
+ NdisMoveMemory(pDst, pSG->aSegs[i].pv, pSG->aSegs[i].cb);
+ pDst += pSG->aSegs[i].cb;
+ }
+ Log4(("vboxNetLwfWinSGtoNB: allocated NBL+NB 0x%p\n", pBufList));
+ pBufList->SourceHandle = pModule->hFilter;
+ }
+ else
+ {
+ LogError(("vboxNetLwfWinSGtoNB: failed to obtain the buffer pointer (size=%u)\n", pSG->cbTotal));
+ NdisAdvanceNetBufferDataStart(pBuffer, cbAlignedFrame, false, NULL); /** @todo why bother? */
+ NdisFreeNetBufferList(pBufList);
+ pBufList = NULL;
+ }
+ }
+ else
+ {
+ LogError(("vboxNetLwfWinSGtoNB: NdisRetreatNetBufferDataStart failed with 0x%x (size=%u)\n", Status, pSG->cbTotal));
+ NdisFreeNetBufferList(pBufList);
+ pBufList = NULL;
+ }
+# else /* !VBOXNETLWF_FIXED_SIZE_POOLS */
+ PNET_BUFFER_LIST pBufList = NULL;
+ ULONG cbMdl = VBOXNETLWF_MAX_FRAME_SIZE(pSG->cbTotal);
+ ULONG uDataOffset = cbMdl - pSG->cbTotal;
+ PUCHAR pDataBuf = (PUCHAR)NdisAllocateMemoryWithTagPriority(pModule->hFilter, cbMdl,
+ VBOXNETLWF_MEM_TAG, NormalPoolPriority);
+ if (pDataBuf)
+ {
+ Log4(("vboxNetLwfWinSGtoNB: allocated data buffer (cb=%u) 0x%p\n", cbMdl, pDataBuf));
+ PMDL pMdl = NdisAllocateMdl(pModule->hFilter, pDataBuf, cbMdl);
+ if (!pMdl)
+ {
+ NdisFreeMemory(pDataBuf, 0, 0);
+ Log4(("vboxNetLwfWinSGtoNB: freed data buffer 0x%p\n", pDataBuf));
+ LogError(("vboxNetLwfWinSGtoNB: failed to allocate an MDL (cb=%u)\n", cbMdl));
+ LogFlow(("<==vboxNetLwfWinSGtoNB: 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(pModule->hPool,
+ 0 /* ContextSize */,
+ 0 /* ContextBackFill */,
+ pMdl,
+ uDataOffset,
+ pSG->cbTotal);
+ if (pBufList)
+ {
+ Log4(("vboxNetLwfWinSGtoNB: allocated NBL+NB 0x%p\n", pBufList));
+ pBufList->SourceHandle = pModule->hFilter;
+ /** @todo Do we need to initialize anything else? */
+ }
+ else
+ {
+ LogError(("vboxNetLwfWinSGtoNB: failed to allocate an NBL+NB\n"));
+ vboxNetLwfWinFreeMdlChain(pMdl);
+ }
+ }
+ else
+ {
+ LogError(("vboxNetLwfWinSGtoNB: failed to allocate data buffer (size=%u)\n", cbMdl));
+ }
+# endif /* !VBOXNETLWF_FIXED_SIZE_POOLS */
+
+#endif /* !VBOXNETLWF_SYNC_SEND */
+ LogFlow(("<==vboxNetLwfWinSGtoNB: return %p\n", pBufList));
+ return pBufList;
+}
+
+/**
+ * @note vboxNetAdpWinNBtoSG() is a copy of this code.
+ */
+static PINTNETSG vboxNetLwfWinNBtoSG(PVBOXNETLWF_MODULE pModule, PNET_BUFFER pNetBuf)
+{
+ ULONG cbPacket = NET_BUFFER_DATA_LENGTH(pNetBuf);
+ ULONG cSegs = vboxNetLwfWinCalcSegments(pNetBuf);
+ /* Allocate and initialize SG */
+ PINTNETSG pSG = (PINTNETSG)NdisAllocateMemoryWithTagPriority(pModule->hFilter,
+ RT_UOFFSETOF_DYN(INTNETSG, aSegs[cSegs]),
+ VBOXNETLWF_MEM_TAG,
+ NormalPoolPriority);
+ AssertReturn(pSG, pSG);
+ Log4(("vboxNetLwfWinNBtoSG: 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)
+ {
+ vboxNetLwfWinDestroySG(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 */
+ vboxNetLwfWinDestroySG(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;
+}
+
+VOID vboxNetLwfWinStatus(IN NDIS_HANDLE hModuleCtx, IN PNDIS_STATUS_INDICATION pIndication)
+{
+ LogFlow(("==>vboxNetLwfWinStatus: module=%p\n", hModuleCtx));
+ PVBOXNETLWF_MODULE pModuleCtx = (PVBOXNETLWF_MODULE)hModuleCtx;
+ Log(("vboxNetLwfWinStatus: Got status indication: %s\n", vboxNetLwfWinStatusToText(pIndication->StatusCode)));
+ switch (pIndication->StatusCode)
+ {
+ case NDIS_STATUS_PACKET_FILTER:
+ vboxNetLwfWinDumpFilterTypes(*(ULONG*)pIndication->StatusBuffer);
+ vboxNetLwfWinOverridePacketFiltersUp(pModuleCtx, (ULONG*)pIndication->StatusBuffer);
+ Log(("vboxNetLwfWinStatus: Reporting status: %s\n", vboxNetLwfWinStatusToText(pIndication->StatusCode)));
+ vboxNetLwfWinDumpFilterTypes(*(ULONG*)pIndication->StatusBuffer);
+ break;
+ case NDIS_STATUS_TASK_OFFLOAD_CURRENT_CONFIG:
+ Log5(("vboxNetLwfWinStatus: offloading currently set to:\n"));
+ vboxNetLwfWinDumpOffloadSettings((PNDIS_OFFLOAD)pIndication->StatusBuffer);
+ vboxNetLwfWinUpdateSavedOffloadConfig(pModuleCtx, (PNDIS_OFFLOAD)pIndication->StatusBuffer);
+ if (ASMAtomicReadBool(&pModuleCtx->fActive))
+ vboxNetLwfWinDisableOffloading((PNDIS_OFFLOAD)pIndication->StatusBuffer);
+ Log5(("vboxNetLwfWinStatus: reporting offloading up as:\n"));
+ vboxNetLwfWinDumpOffloadSettings((PNDIS_OFFLOAD)pIndication->StatusBuffer);
+ break;
+ }
+ NdisFIndicateStatus(pModuleCtx->hFilter, pIndication);
+ LogFlow(("<==vboxNetLwfWinStatus\n"));
+}
+
+static bool vboxNetLwfWinForwardToIntNet(PVBOXNETLWF_MODULE pModuleCtx, PNET_BUFFER_LIST pBufLists, uint32_t fSrc)
+{
+ /* We must not forward anything to the trunk unless it is ready to receive. */
+ if (!ASMAtomicReadBool(&pModuleCtx->fActive))
+ {
+ Log(("vboxNetLwfWinForwardToIntNet: trunk is inactive, won't forward\n"));
+ return false;
+ }
+ /* Some NPF protocols make NDIS to loop back packets at miniport level, we must ignore those. */
+ if (NdisTestNblFlag(pBufLists, NDIS_NBL_FLAGS_IS_LOOPBACK_PACKET))
+ {
+ if (pBufLists->SourceHandle == pModuleCtx->hFilter && !pModuleCtx->fPassVmTrafficToHost)
+ {
+ /* Drop the packets we've injected. */
+ vboxNetLwfWinDumpPackets("vboxNetLwfWinForwardToIntNet: dropping loopback", pBufLists);
+ return true;
+ }
+ vboxNetLwfWinDumpPackets("vboxNetLwfWinForwardToIntNet: passing through loopback", pBufLists);
+ return false;
+ }
+
+ AssertReturn(pModuleCtx->pNetFlt, false);
+ AssertReturn(pModuleCtx->pNetFlt->pSwitchPort, false);
+ AssertReturn(pModuleCtx->pNetFlt->pSwitchPort->pfnRecv, false);
+ LogFlow(("==>vboxNetLwfWinForwardToIntNet: module=%p\n", pModuleCtx));
+ Assert(pBufLists); /* The chain must contain at least one list */
+ Assert(NET_BUFFER_LIST_NEXT_NBL(pBufLists) == NULL); /* The caller is supposed to unlink the list from the chain */
+ /*
+ * Even if NBL contains more than one buffer we are prepared to deal with it.
+ * When any of buffers should not be dropped we keep the whole list. It is
+ * better to leak some "unexpected" packets to the wire/host than to loose any.
+ */
+ bool fDropIt = false;
+ bool fDontDrop = false;
+ int nLists = 0;
+ for (PNET_BUFFER_LIST pList = pBufLists; pList; pList = NET_BUFFER_LIST_NEXT_NBL(pList))
+ {
+ int nBuffers = 0;
+ nLists++;
+ for (PNET_BUFFER pBuf = NET_BUFFER_LIST_FIRST_NB(pList); pBuf; pBuf = NET_BUFFER_NEXT_NB(pBuf))
+ {
+ nBuffers++;
+ PINTNETSG pSG = vboxNetLwfWinNBtoSG(pModuleCtx, pBuf);
+ if (pSG)
+ {
+ vboxNetLwfWinDumpPacket(pSG, (fSrc & INTNETTRUNKDIR_WIRE)?"intnet <-- wire":"intnet <-- host");
+ /* A bit paranoid, but we do not use any locks, so... */
+ if (ASMAtomicReadBool(&pModuleCtx->fActive))
+ if (pModuleCtx->pNetFlt->pSwitchPort->pfnRecv(pModuleCtx->pNetFlt->pSwitchPort, NULL, pSG, fSrc))
+ fDropIt = true;
+ else
+ fDontDrop = true;
+ vboxNetLwfWinDestroySG(pSG);
+ }
+ }
+ Log(("vboxNetLwfWinForwardToIntNet: list=%d buffers=%d\n", nLists, nBuffers));
+ }
+ Log(("vboxNetLwfWinForwardToIntNet: lists=%d drop=%s don't=%s\n", nLists, fDropIt ? "true":"false", fDontDrop ? "true":"false"));
+
+ /* If the host (and the user) wants to see all packets we must not drop any. */
+ if (pModuleCtx->fPassVmTrafficToHost && vboxNetLwfWinIsPromiscuous(pModuleCtx))
+ fDropIt = false;
+
+ LogFlow(("<==vboxNetLwfWinForwardToIntNet: return '%s'\n",
+ fDropIt ? (fDontDrop ? "do not drop (some)" : "drop it") : "do not drop (any)"));
+ return fDropIt && !fDontDrop; /* Drop the list if ALL its buffers are being dropped! */
+}
+
+DECLINLINE(bool) vboxNetLwfWinIsRunning(PVBOXNETLWF_MODULE pModule)
+{
+ Log(("vboxNetLwfWinIsRunning: state=%d\n", ASMAtomicReadU32(&pModule->enmState)));
+ return ASMAtomicReadU32(&pModule->enmState) == LwfState_Running;
+}
+
+VOID vboxNetLwfWinSendNetBufferLists(IN NDIS_HANDLE hModuleCtx, IN PNET_BUFFER_LIST pBufLists, IN NDIS_PORT_NUMBER nPort, IN ULONG fFlags)
+{
+ LogFlow(("==>vboxNetLwfWinSendNetBufferLists: module=%p\n", hModuleCtx));
+ PVBOXNETLWF_MODULE pModule = (PVBOXNETLWF_MODULE)hModuleCtx;
+ vboxNetLwfWinDumpPackets("vboxNetLwfWinSendNetBufferLists: got", pBufLists);
+
+ if (!ASMAtomicReadBool(&pModule->fActive))
+ {
+ /*
+ * The trunk is inactive, jusp pass along all packets to the next
+ * underlying driver.
+ */
+ NdisFSendNetBufferLists(pModule->hFilter, pBufLists, nPort, fFlags);
+ return;
+ }
+
+ if (vboxNetLwfWinIsRunning(pModule))
+ {
+ PNET_BUFFER_LIST pNext = NULL;
+ PNET_BUFFER_LIST pDropHead = NULL;
+ PNET_BUFFER_LIST pDropTail = NULL;
+ PNET_BUFFER_LIST pPassHead = NULL;
+ PNET_BUFFER_LIST pPassTail = NULL;
+ for (PNET_BUFFER_LIST pList = pBufLists; pList; pList = pNext)
+ {
+ pNext = NET_BUFFER_LIST_NEXT_NBL(pList);
+ NET_BUFFER_LIST_NEXT_NBL(pList) = NULL; /* Unlink */
+ if (vboxNetLwfWinForwardToIntNet(pModule, pList, INTNETTRUNKDIR_HOST))
+ {
+ NET_BUFFER_LIST_STATUS(pList) = NDIS_STATUS_SUCCESS;
+ if (pDropHead)
+ {
+ NET_BUFFER_LIST_NEXT_NBL(pDropTail) = pList;
+ pDropTail = pList;
+ }
+ else
+ pDropHead = pDropTail = pList;
+ }
+ else
+ {
+ if (pPassHead)
+ {
+ NET_BUFFER_LIST_NEXT_NBL(pPassTail) = pList;
+ pPassTail = pList;
+ }
+ else
+ pPassHead = pPassTail = pList;
+ }
+ }
+ Assert((pBufLists == pPassHead) || (pBufLists == pDropHead));
+ if (pPassHead)
+ {
+ vboxNetLwfWinDumpPackets("vboxNetLwfWinSendNetBufferLists: passing down", pPassHead);
+ NdisFSendNetBufferLists(pModule->hFilter, pBufLists, nPort, fFlags);
+ }
+ if (pDropHead)
+ {
+ vboxNetLwfWinDumpPackets("vboxNetLwfWinSendNetBufferLists: consumed", pDropHead);
+ NdisFSendNetBufferListsComplete(pModule->hFilter, pDropHead,
+ fFlags & NDIS_SEND_FLAGS_DISPATCH_LEVEL ? NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL : 0);
+ }
+ }
+ else
+ {
+ for (PNET_BUFFER_LIST pList = pBufLists; pList; pList = NET_BUFFER_LIST_NEXT_NBL(pList))
+ {
+ NET_BUFFER_LIST_STATUS(pList) = NDIS_STATUS_PAUSED;
+ }
+ vboxNetLwfWinDumpPackets("vboxNetLwfWinSendNetBufferLists: consumed", pBufLists);
+ NdisFSendNetBufferListsComplete(pModule->hFilter, pBufLists,
+ fFlags & NDIS_SEND_FLAGS_DISPATCH_LEVEL ? NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL : 0);
+
+ }
+ LogFlow(("<==vboxNetLwfWinSendNetBufferLists\n"));
+}
+
+VOID vboxNetLwfWinSendNetBufferListsComplete(IN NDIS_HANDLE hModuleCtx, IN PNET_BUFFER_LIST pBufLists, IN ULONG fFlags)
+{
+ LogFlow(("==>vboxNetLwfWinSendNetBufferListsComplete: module=%p\n", hModuleCtx));
+ PVBOXNETLWF_MODULE pModule = (PVBOXNETLWF_MODULE)hModuleCtx;
+ PNET_BUFFER_LIST pList = pBufLists;
+ PNET_BUFFER_LIST pNextList;
+ PNET_BUFFER_LIST pPrevList = NULL;
+ while (pList)
+ {
+ pNextList = NET_BUFFER_LIST_NEXT_NBL(pList);
+ if (pList->SourceHandle == pModule->hFilter)
+ {
+ /* We allocated this NET_BUFFER_LIST, let's free it up */
+ Assert(NET_BUFFER_LIST_FIRST_NB(pList));
+ Assert(NET_BUFFER_FIRST_MDL(NET_BUFFER_LIST_FIRST_NB(pList)));
+ /*
+ * All our NBLs hold a single NB each, no need to iterate over a list.
+ * There is no need to free an associated NB explicitly either, as it was
+ * preallocated with NBL structure.
+ */
+ Assert(!NET_BUFFER_NEXT_NB(NET_BUFFER_LIST_FIRST_NB(pList)));
+ vboxNetLwfWinFreeMdlChain(NET_BUFFER_FIRST_MDL(NET_BUFFER_LIST_FIRST_NB(pList)));
+ /* Unlink this list from the chain */
+ if (pPrevList)
+ NET_BUFFER_LIST_NEXT_NBL(pPrevList) = pNextList;
+ else
+ pBufLists = pNextList;
+ Log(("vboxNetLwfWinSendNetBufferListsComplete: our list %p, next=%p, previous=%p, head=%p\n", pList, pNextList, pPrevList, pBufLists));
+ NdisFreeNetBufferList(pList);
+#ifdef VBOXNETLWF_SYNC_SEND
+ Log4(("vboxNetLwfWinSendNetBufferListsComplete: freed NBL+NB 0x%p\n", pList));
+ KeSetEvent(&pModule->EventWire, 0, FALSE);
+#else /* !VBOXNETLWF_SYNC_SEND */
+ Log4(("vboxNetLwfWinSendNetBufferListsComplete: freed NBL+NB+MDL+Data 0x%p\n", pList));
+ Assert(ASMAtomicReadS32(&pModule->cPendingBuffers) > 0);
+ if (ASMAtomicDecS32(&pModule->cPendingBuffers) == 0)
+ NdisSetEvent(&pModule->EventSendComplete);
+#endif /* !VBOXNETLWF_SYNC_SEND */
+ }
+ else
+ {
+ pPrevList = pList;
+ Log(("vboxNetLwfWinSendNetBufferListsComplete: passing list %p, next=%p, previous=%p, head=%p\n", pList, pNextList, pPrevList, pBufLists));
+ }
+ pList = pNextList;
+ }
+ if (pBufLists)
+ {
+ /* There are still lists remaining in the chain, pass'em up */
+ NdisFSendNetBufferListsComplete(pModule->hFilter, pBufLists, fFlags);
+ }
+ LogFlow(("<==vboxNetLwfWinSendNetBufferListsComplete\n"));
+}
+
+VOID vboxNetLwfWinReceiveNetBufferLists(IN NDIS_HANDLE hModuleCtx,
+ IN PNET_BUFFER_LIST pBufLists,
+ IN NDIS_PORT_NUMBER nPort,
+ IN ULONG nBufLists,
+ IN ULONG fFlags)
+{
+ /// @todo Do we need loopback handling?
+ LogFlow(("==>vboxNetLwfWinReceiveNetBufferLists: module=%p\n", hModuleCtx));
+ PVBOXNETLWF_MODULE pModule = (PVBOXNETLWF_MODULE)hModuleCtx;
+ vboxNetLwfWinDumpPackets("vboxNetLwfWinReceiveNetBufferLists: got", pBufLists);
+
+ if (!ASMAtomicReadBool(&pModule->fActive))
+ {
+ /*
+ * The trunk is inactive, just pass along all packets to the next
+ * overlying driver.
+ */
+ NdisFIndicateReceiveNetBufferLists(pModule->hFilter, pBufLists, nPort, nBufLists, fFlags);
+ LogFlow(("<==vboxNetLwfWinReceiveNetBufferLists: inactive trunk\n"));
+ return;
+ }
+
+ if (vboxNetLwfWinIsRunning(pModule))
+ {
+ if (NDIS_TEST_RECEIVE_CANNOT_PEND(fFlags))
+ {
+ for (PNET_BUFFER_LIST pList = pBufLists; pList; pList = NET_BUFFER_LIST_NEXT_NBL(pList))
+ {
+ PNET_BUFFER_LIST pNext = NET_BUFFER_LIST_NEXT_NBL(pList);
+ NET_BUFFER_LIST_NEXT_NBL(pList) = NULL; /* Unlink temporarily */
+ if (!vboxNetLwfWinForwardToIntNet(pModule, pList, INTNETTRUNKDIR_WIRE))
+ {
+ vboxNetLwfWinDumpPackets("vboxNetLwfWinReceiveNetBufferLists: passing up", pList);
+ NdisFIndicateReceiveNetBufferLists(pModule->hFilter, pList, nPort, nBufLists, fFlags);
+ }
+ NET_BUFFER_LIST_NEXT_NBL(pList) = pNext; /* Restore the link */
+ }
+ }
+ else
+ {
+ /* We collect dropped NBLs in a separate list in order to "return" them. */
+ PNET_BUFFER_LIST pNext = NULL;
+ PNET_BUFFER_LIST pDropHead = NULL;
+ PNET_BUFFER_LIST pDropTail = NULL;
+ PNET_BUFFER_LIST pPassHead = NULL;
+ PNET_BUFFER_LIST pPassTail = NULL;
+ ULONG nDrop = 0, nPass = 0;
+ for (PNET_BUFFER_LIST pList = pBufLists; pList; pList = pNext)
+ {
+ pNext = NET_BUFFER_LIST_NEXT_NBL(pList);
+ NET_BUFFER_LIST_NEXT_NBL(pList) = NULL; /* Unlink */
+ if (vboxNetLwfWinForwardToIntNet(pModule, pList, INTNETTRUNKDIR_WIRE))
+ {
+ if (nDrop++)
+ {
+ NET_BUFFER_LIST_NEXT_NBL(pDropTail) = pList;
+ pDropTail = pList;
+ }
+ else
+ pDropHead = pDropTail = pList;
+ }
+ else
+ {
+ if (nPass++)
+ {
+ NET_BUFFER_LIST_NEXT_NBL(pPassTail) = pList;
+ pPassTail = pList;
+ }
+ else
+ pPassHead = pPassTail = pList;
+ }
+ }
+ Assert((pBufLists == pPassHead) || (pBufLists == pDropHead));
+ Assert(nDrop + nPass == nBufLists);
+ if (pPassHead)
+ {
+ vboxNetLwfWinDumpPackets("vboxNetLwfWinReceiveNetBufferLists: passing up", pPassHead);
+ NdisFIndicateReceiveNetBufferLists(pModule->hFilter, pPassHead, nPort, nPass, fFlags);
+ }
+ if (pDropHead)
+ {
+ vboxNetLwfWinDumpPackets("vboxNetLwfWinReceiveNetBufferLists: consumed", pDropHead);
+ NdisFReturnNetBufferLists(pModule->hFilter, pDropHead,
+ fFlags & NDIS_RECEIVE_FLAGS_DISPATCH_LEVEL ? NDIS_RETURN_FLAGS_DISPATCH_LEVEL : 0);
+ }
+ }
+
+ }
+ else
+ {
+ vboxNetLwfWinDumpPackets("vboxNetLwfWinReceiveNetBufferLists: consumed", pBufLists);
+ if ((fFlags & NDIS_RECEIVE_FLAGS_RESOURCES) == 0)
+ NdisFReturnNetBufferLists(pModule->hFilter, pBufLists,
+ fFlags & NDIS_RECEIVE_FLAGS_DISPATCH_LEVEL ? NDIS_RETURN_FLAGS_DISPATCH_LEVEL : 0);
+ }
+ LogFlow(("<==vboxNetLwfWinReceiveNetBufferLists\n"));
+}
+
+VOID vboxNetLwfWinReturnNetBufferLists(IN NDIS_HANDLE hModuleCtx, IN PNET_BUFFER_LIST pBufLists, IN ULONG fFlags)
+{
+ LogFlow(("==>vboxNetLwfWinReturnNetBufferLists: module=%p\n", hModuleCtx));
+ PVBOXNETLWF_MODULE pModule = (PVBOXNETLWF_MODULE)hModuleCtx;
+ PNET_BUFFER_LIST pList = pBufLists;
+ PNET_BUFFER_LIST pNextList;
+ PNET_BUFFER_LIST pPrevList = NULL;
+ /** @todo Move common part into a separate function to be used by vboxNetLwfWinSendNetBufferListsComplete() as well */
+ while (pList)
+ {
+ pNextList = NET_BUFFER_LIST_NEXT_NBL(pList);
+ if (pList->SourceHandle == pModule->hFilter)
+ {
+ /* We allocated this NET_BUFFER_LIST, let's free it up */
+ Assert(NET_BUFFER_LIST_FIRST_NB(pList));
+ Assert(NET_BUFFER_FIRST_MDL(NET_BUFFER_LIST_FIRST_NB(pList)));
+ /*
+ * All our NBLs hold a single NB each, no need to iterate over a list.
+ * There is no need to free an associated NB explicitly either, as it was
+ * preallocated with NBL structure.
+ */
+ vboxNetLwfWinFreeMdlChain(NET_BUFFER_FIRST_MDL(NET_BUFFER_LIST_FIRST_NB(pList)));
+ /* Unlink this list from the chain */
+ if (pPrevList)
+ NET_BUFFER_LIST_NEXT_NBL(pPrevList) = pNextList;
+ else
+ pBufLists = pNextList;
+ NdisFreeNetBufferList(pList);
+#ifdef VBOXNETLWF_SYNC_SEND
+ Log4(("vboxNetLwfWinReturnNetBufferLists: freed NBL+NB 0x%p\n", pList));
+ KeSetEvent(&pModule->EventHost, 0, FALSE);
+#else /* !VBOXNETLWF_SYNC_SEND */
+ Log4(("vboxNetLwfWinReturnNetBufferLists: freed NBL+NB+MDL+Data 0x%p\n", pList));
+ Assert(ASMAtomicReadS32(&pModule->cPendingBuffers) > 0);
+ if (ASMAtomicDecS32(&pModule->cPendingBuffers) == 0)
+ NdisSetEvent(&pModule->EventSendComplete);
+#endif /* !VBOXNETLWF_SYNC_SEND */
+ }
+ else
+ pPrevList = pList;
+ pList = pNextList;
+ }
+ if (pBufLists)
+ {
+ /* There are still lists remaining in the chain, pass'em up */
+ NdisFReturnNetBufferLists(pModule->hFilter, pBufLists, fFlags);
+ }
+ LogFlow(("<==vboxNetLwfWinReturnNetBufferLists\n"));
+}
+
+/**
+ * register the filter driver
+ */
+DECLHIDDEN(NDIS_STATUS) vboxNetLwfWinRegister(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegistryPathStr)
+{
+ RT_NOREF1(pRegistryPathStr);
+ NDIS_FILTER_DRIVER_CHARACTERISTICS FChars;
+ NDIS_STRING FriendlyName;
+ NDIS_STRING UniqueName;
+ NDIS_STRING ServiceName;
+
+ NdisInitUnicodeString(&FriendlyName, VBOXNETLWF_NAME_FRIENDLY);
+ NdisInitUnicodeString(&UniqueName, VBOXNETLWF_NAME_UNIQUE);
+ NdisInitUnicodeString(&ServiceName, VBOXNETLWF_NAME_SERVICE);
+
+ NdisZeroMemory(&FChars, sizeof (FChars));
+
+ FChars.Header.Type = NDIS_OBJECT_TYPE_FILTER_DRIVER_CHARACTERISTICS;
+ FChars.Header.Size = sizeof(NDIS_FILTER_DRIVER_CHARACTERISTICS);
+ FChars.Header.Revision = NDIS_FILTER_CHARACTERISTICS_REVISION_1;
+
+ FChars.MajorNdisVersion = VBOXNETLWF_VERSION_NDIS_MAJOR;
+ FChars.MinorNdisVersion = VBOXNETLWF_VERSION_NDIS_MINOR;
+
+ FChars.FriendlyName = FriendlyName;
+ FChars.UniqueName = UniqueName;
+ FChars.ServiceName = ServiceName;
+
+ /* Mandatory functions */
+ FChars.AttachHandler = vboxNetLwfWinAttach;
+ FChars.DetachHandler = vboxNetLwfWinDetach;
+ FChars.RestartHandler = vboxNetLwfWinRestart;
+ FChars.PauseHandler = vboxNetLwfWinPause;
+
+ /* Optional functions, non changeble at run-time */
+ FChars.OidRequestHandler = vboxNetLwfWinOidRequest;
+ FChars.OidRequestCompleteHandler = vboxNetLwfWinOidRequestComplete;
+ //FChars.CancelOidRequestHandler = vboxNetLwfWinCancelOidRequest;
+ FChars.StatusHandler = vboxNetLwfWinStatus;
+ //FChars.NetPnPEventHandler = vboxNetLwfWinPnPEvent;
+
+ /* Datapath functions */
+ FChars.SendNetBufferListsHandler = vboxNetLwfWinSendNetBufferLists;
+ FChars.SendNetBufferListsCompleteHandler = vboxNetLwfWinSendNetBufferListsComplete;
+ FChars.ReceiveNetBufferListsHandler = vboxNetLwfWinReceiveNetBufferLists;
+ FChars.ReturnNetBufferListsHandler = vboxNetLwfWinReturnNetBufferLists;
+
+ pDriverObject->DriverUnload = vboxNetLwfWinUnloadDriver;
+
+ NDIS_STATUS Status;
+ g_VBoxNetLwfGlobals.hFilterDriver = NULL;
+ Log(("vboxNetLwfWinRegister: registering filter driver...\n"));
+ Status = NdisFRegisterFilterDriver(pDriverObject,
+ (NDIS_HANDLE)&g_VBoxNetLwfGlobals,
+ &FChars,
+ &g_VBoxNetLwfGlobals.hFilterDriver);
+ Assert(Status == STATUS_SUCCESS);
+ if (Status == STATUS_SUCCESS)
+ {
+ Log(("vboxNetLwfWinRegister: successfully registered filter driver; registering device...\n"));
+ Status = vboxNetLwfWinDevCreate(&g_VBoxNetLwfGlobals);
+ Assert(Status == STATUS_SUCCESS);
+ Log(("vboxNetLwfWinRegister: vboxNetLwfWinDevCreate() returned 0x%x\n", Status));
+ }
+ else
+ {
+ LogError(("vboxNetLwfWinRegister: failed to register filter driver, status=0x%x", Status));
+ }
+ return Status;
+}
+
+static int vboxNetLwfWinStartInitIdcThread()
+{
+ int rc = VERR_INVALID_STATE;
+
+ if (ASMAtomicCmpXchgU32(&g_VBoxNetLwfGlobals.enmIdcState, LwfIdcState_Connecting, LwfIdcState_Disconnected))
+ {
+ Log(("vboxNetLwfWinStartInitIdcThread: IDC state change Diconnected -> Connecting\n"));
+
+ NTSTATUS Status = PsCreateSystemThread(&g_VBoxNetLwfGlobals.hInitIdcThread,
+ THREAD_ALL_ACCESS,
+ NULL,
+ NULL,
+ NULL,
+ vboxNetLwfWinInitIdcWorker,
+ &g_VBoxNetLwfGlobals);
+ Log(("vboxNetLwfWinStartInitIdcThread: create IDC initialization thread, status=0x%x\n", Status));
+ if (Status != STATUS_SUCCESS)
+ {
+ LogError(("vboxNetLwfWinStartInitIdcThread: IDC initialization failed (system thread creation, status=0x%x)\n", Status));
+ /*
+ * We failed to init IDC and there will be no second chance.
+ */
+ Log(("vboxNetLwfWinStartInitIdcThread: IDC state change Connecting -> Diconnected\n"));
+ ASMAtomicWriteU32(&g_VBoxNetLwfGlobals.enmIdcState, LwfIdcState_Disconnected);
+ }
+ rc = RTErrConvertFromNtStatus(Status);
+ }
+ return rc;
+}
+
+static void vboxNetLwfWinStopInitIdcThread()
+{
+}
+
+
+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;
+
+ /* the idc registration is initiated via IOCTL since our driver
+ * can be loaded when the VBoxDrv is not in case we are a Ndis IM driver */
+ rc = vboxNetLwfWinInitBase();
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ NdisZeroMemory(&g_VBoxNetLwfGlobals, sizeof (g_VBoxNetLwfGlobals));
+ RTListInit(&g_VBoxNetLwfGlobals.listModules);
+ NdisAllocateSpinLock(&g_VBoxNetLwfGlobals.Lock);
+ /*
+ * We choose to ignore IDC initialization errors here because if we fail to load
+ * our filter the upper protocols won't bind to the associated adapter, causing
+ * network failure at the host. Better to have non-working filter than broken
+ * networking on the host.
+ */
+ rc = vboxNetLwfWinStartInitIdcThread();
+ AssertRC(rc);
+
+ Status = vboxNetLwfWinRegister(pDriverObject, pRegistryPath);
+ Assert(Status == STATUS_SUCCESS);
+ if (Status == NDIS_STATUS_SUCCESS)
+ {
+ Log(("NETLWF: started successfully\n"));
+ return STATUS_SUCCESS;
+ }
+ NdisFreeSpinLock(&g_VBoxNetLwfGlobals.Lock);
+ vboxNetLwfWinFini();
+ }
+ else
+ {
+ Status = NDIS_STATUS_FAILURE;
+ }
+
+ return Status;
+}
+
+
+static VOID vboxNetLwfWinUnloadDriver(IN PDRIVER_OBJECT pDriver)
+{
+ RT_NOREF1(pDriver);
+ LogFlow(("==>vboxNetLwfWinUnloadDriver: driver=%p\n", pDriver));
+ vboxNetLwfWinDevDestroy(&g_VBoxNetLwfGlobals);
+ NdisFDeregisterFilterDriver(g_VBoxNetLwfGlobals.hFilterDriver);
+ NdisFreeSpinLock(&g_VBoxNetLwfGlobals.Lock);
+ LogFlow(("<==vboxNetLwfWinUnloadDriver\n"));
+ vboxNetLwfWinFini();
+}
+
+static const char *vboxNetLwfWinIdcStateToText(uint32_t enmState)
+{
+ switch (enmState)
+ {
+ case LwfIdcState_Disconnected: return "Disconnected";
+ case LwfIdcState_Connecting: return "Connecting";
+ case LwfIdcState_Connected: return "Connected";
+ case LwfIdcState_Stopping: return "Stopping";
+ }
+ return "Unknown";
+}
+
+static VOID vboxNetLwfWinInitIdcWorker(PVOID pvContext)
+{
+ int rc;
+ PVBOXNETLWFGLOBALS pGlobals = (PVBOXNETLWFGLOBALS)pvContext;
+
+ while (ASMAtomicReadU32(&pGlobals->enmIdcState) == LwfIdcState_Connecting)
+ {
+ rc = vboxNetFltInitIdc(&g_VBoxNetFltGlobals);
+ if (RT_SUCCESS(rc))
+ {
+ if (!ASMAtomicCmpXchgU32(&pGlobals->enmIdcState, LwfIdcState_Connected, LwfIdcState_Connecting))
+ {
+ /* The state has been changed (the only valid transition is to "Stopping"), undo init */
+ rc = vboxNetFltTryDeleteIdc(&g_VBoxNetFltGlobals);
+ Log(("vboxNetLwfWinInitIdcWorker: state change (Connecting -> %s) while initializing IDC, deleted IDC, rc=0x%x\n",
+ vboxNetLwfWinIdcStateToText(ASMAtomicReadU32(&pGlobals->enmIdcState)), rc));
+ }
+ else
+ {
+ Log(("vboxNetLwfWinInitIdcWorker: IDC state change Connecting -> Connected\n"));
+ }
+ }
+ else
+ {
+ LARGE_INTEGER WaitIn100nsUnits;
+ WaitIn100nsUnits.QuadPart = -(LONGLONG)10000000; /* 1 sec */
+ KeDelayExecutionThread(KernelMode, FALSE /* non-alertable */, &WaitIn100nsUnits);
+ }
+ }
+ PsTerminateSystemThread(STATUS_SUCCESS);
+}
+
+static int vboxNetLwfWinTryFiniIdc()
+{
+ int rc = VINF_SUCCESS;
+ NTSTATUS Status;
+ PKTHREAD pThread = NULL;
+ uint32_t enmPrevState = ASMAtomicXchgU32(&g_VBoxNetLwfGlobals.enmIdcState, LwfIdcState_Stopping);
+
+ Log(("vboxNetLwfWinTryFiniIdc: IDC state change %s -> Stopping\n", vboxNetLwfWinIdcStateToText(enmPrevState)));
+
+ switch (enmPrevState)
+ {
+ case LwfIdcState_Disconnected:
+ /* Have not even attempted to connect -- nothing to do. */
+ break;
+ case LwfIdcState_Stopping:
+ /* Impossible, but another thread is alreading doing FiniIdc, bail out */
+ LogError(("vboxNetLwfWinTryFiniIdc: called in 'Stopping' state\n"));
+ rc = VERR_INVALID_STATE;
+ break;
+ case LwfIdcState_Connecting:
+ /* the worker thread is running, let's wait for it to stop */
+ Status = ObReferenceObjectByHandle(g_VBoxNetLwfGlobals.hInitIdcThread,
+ THREAD_ALL_ACCESS, NULL, KernelMode,
+ (PVOID*)&pThread, NULL);
+ if (Status == STATUS_SUCCESS)
+ {
+ KeWaitForSingleObject(pThread, Executive, KernelMode, FALSE, NULL);
+ ObDereferenceObject(pThread);
+ }
+ else
+ {
+ LogError(("vboxNetLwfWinTryFiniIdc: ObReferenceObjectByHandle(%p) failed with 0x%x\n",
+ g_VBoxNetLwfGlobals.hInitIdcThread, Status));
+ }
+ rc = RTErrConvertFromNtStatus(Status);
+ break;
+ case LwfIdcState_Connected:
+ /* the worker succeeded in IDC init and terminated */
+ rc = vboxNetFltTryDeleteIdc(&g_VBoxNetFltGlobals);
+ Log(("vboxNetLwfWinTryFiniIdc: deleted IDC, rc=0x%x\n", rc));
+ break;
+ }
+ return rc;
+}
+
+static void vboxNetLwfWinFiniBase()
+{
+ vboxNetFltDeleteGlobals(&g_VBoxNetFltGlobals);
+
+ /*
+ * Undo the work done during start (in reverse order).
+ */
+ memset(&g_VBoxNetFltGlobals, 0, sizeof(g_VBoxNetFltGlobals));
+
+ RTLogDestroy(RTLogRelSetDefaultInstance(NULL));
+ RTLogDestroy(RTLogSetDefaultInstance(NULL));
+
+ RTR0Term();
+}
+
+static int vboxNetLwfWinInitBase()
+{
+ int rc = RTR0Init(0);
+ if (!RT_SUCCESS(rc))
+ return rc;
+
+ memset(&g_VBoxNetFltGlobals, 0, sizeof(g_VBoxNetFltGlobals));
+ rc = vboxNetFltInitGlobals(&g_VBoxNetFltGlobals);
+ if (!RT_SUCCESS(rc))
+ RTR0Term();
+
+ return rc;
+}
+
+static int vboxNetLwfWinFini()
+{
+ int rc = vboxNetLwfWinTryFiniIdc();
+ if (RT_SUCCESS(rc))
+ {
+ vboxNetLwfWinFiniBase();
+ }
+ return rc;
+}
+
+
+/*
+ *
+ * The OS specific interface definition
+ *
+ */
+
+
+bool vboxNetFltOsMaybeRediscovered(PVBOXNETFLTINS pThis)
+{
+ LogFlow(("==>vboxNetFltOsMaybeRediscovered: instance=%p\n", pThis));
+ LogFlow(("<==vboxNetFltOsMaybeRediscovered: return %RTbool\n", !ASMAtomicUoReadBool(&pThis->fDisconnectedFromHost)));
+ /* AttachToInterface true if disconnected */
+ return !ASMAtomicUoReadBool(&pThis->fDisconnectedFromHost);
+}
+
+int vboxNetFltPortOsXmit(PVBOXNETFLTINS pThis, void *pvIfData, PINTNETSG pSG, uint32_t fDst)
+{
+ RT_NOREF1(pvIfData);
+ int rc = VINF_SUCCESS;
+
+ PVBOXNETLWF_MODULE pModule = (PVBOXNETLWF_MODULE)pThis->u.s.WinIf.hModuleCtx;
+ LogFlow(("==>vboxNetFltPortOsXmit: instance=%p module=%p\n", pThis, pModule));
+ if (!pModule)
+ {
+ LogFlow(("<==vboxNetFltPortOsXmit: pModule is null, return %d\n", VERR_INTERNAL_ERROR));
+ return VERR_INTERNAL_ERROR;
+ }
+ /* Prevent going into "paused" state until all transmissions have been completed. */
+ NDIS_WAIT_FOR_MUTEX(&pModule->InTransmit);
+ /* Ignore all sends if the stack is paused or being paused, etc... */
+ if (!vboxNetLwfWinIsRunning(pModule))
+ {
+ NDIS_RELEASE_MUTEX(&pModule->InTransmit);
+ return VINF_SUCCESS;
+ }
+
+ vboxNetLwfWinDumpPacket(pSG, !(fDst & INTNETTRUNKDIR_WIRE) ? "intnet --> host"
+ : !(fDst & INTNETTRUNKDIR_HOST) ? "intnet --> wire" : "intnet --> all");
+
+ /*
+ * There are two possible strategies to deal with incoming SGs:
+ * 1) make a copy of data and complete asynchronously;
+ * 2) complete synchronously using the original data buffers.
+ * Before we consider implementing (1) it is quite interesting to see
+ * how well (2) performs. So we block until our requests are complete.
+ * Actually there is third possibility -- to use SG retain/release
+ * callbacks, but those seem not be fully implemented yet.
+ * Note that ansynchronous completion will require different implementation
+ * of vboxNetLwfWinPause(), not relying on InTransmit mutex.
+ */
+#ifdef VBOXNETLWF_SYNC_SEND
+ PVOID aEvents[2]; /* To wire and to host */
+ ULONG nEvents = 0;
+ LARGE_INTEGER timeout;
+ timeout.QuadPart = -(LONGLONG)10000000; /* 1 sec */
+#endif /* VBOXNETLWF_SYNC_SEND */
+ if (fDst & INTNETTRUNKDIR_WIRE)
+ {
+ PNET_BUFFER_LIST pBufList = vboxNetLwfWinSGtoNB(pModule, pSG);
+ if (pBufList)
+ {
+ vboxNetLwfWinDumpPackets("vboxNetFltPortOsXmit: sending down", pBufList);
+#ifdef VBOXNETLWF_SYNC_SEND
+ aEvents[nEvents++] = &pModule->EventWire;
+#else /* !VBOXNETLWF_SYNC_SEND */
+ if (ASMAtomicIncS32(&pModule->cPendingBuffers) == 1)
+ NdisResetEvent(&pModule->EventSendComplete);
+#endif /* !VBOXNETLWF_SYNC_SEND */
+ NdisFSendNetBufferLists(pModule->hFilter, pBufList, NDIS_DEFAULT_PORT_NUMBER, 0); /** @todo sendFlags! */
+ }
+ }
+ if (fDst & INTNETTRUNKDIR_HOST)
+ {
+ PNET_BUFFER_LIST pBufList = vboxNetLwfWinSGtoNB(pModule, pSG);
+ if (pBufList)
+ {
+ vboxNetLwfWinDumpPackets("vboxNetFltPortOsXmit: sending up", pBufList);
+#ifdef VBOXNETLWF_SYNC_SEND
+ aEvents[nEvents++] = &pModule->EventHost;
+#else /* !VBOXNETLWF_SYNC_SEND */
+ if (ASMAtomicIncS32(&pModule->cPendingBuffers) == 1)
+ NdisResetEvent(&pModule->EventSendComplete);
+#endif /* !VBOXNETLWF_SYNC_SEND */
+ NdisFIndicateReceiveNetBufferLists(pModule->hFilter, pBufList, NDIS_DEFAULT_PORT_NUMBER, 1, 0);
+ }
+ }
+#ifdef VBOXNETLWF_SYNC_SEND
+ if (nEvents)
+ {
+ NTSTATUS Status = KeWaitForMultipleObjects(nEvents, aEvents, WaitAll, Executive, KernelMode, FALSE, &timeout, NULL);
+ if (Status != STATUS_SUCCESS)
+ {
+ LogError(("vboxNetFltPortOsXmit: KeWaitForMultipleObjects() failed with 0x%x\n", Status));
+ if (Status == STATUS_TIMEOUT)
+ rc = VERR_TIMEOUT;
+ else
+ rc = RTErrConvertFromNtStatus(Status);
+ }
+ }
+#endif /* VBOXNETLWF_SYNC_SEND */
+ NDIS_RELEASE_MUTEX(&pModule->InTransmit);
+
+ LogFlow(("<==vboxNetFltPortOsXmit: return %d\n", rc));
+ return rc;
+}
+
+
+NDIS_IO_WORKITEM_FUNCTION vboxNetLwfWinToggleOffloading;
+
+VOID vboxNetLwfWinToggleOffloading(PVOID WorkItemContext, NDIS_HANDLE NdisIoWorkItemHandle)
+{
+ /* WARNING! Call this with IRQL=Passive! */
+ RT_NOREF1(NdisIoWorkItemHandle);
+ PVBOXNETLWF_MODULE pModuleCtx = (PVBOXNETLWF_MODULE)WorkItemContext;
+
+ if (ASMAtomicReadBool(&pModuleCtx->fActive))
+ {
+ /* Disable offloading temporarily by indicating offload config change. */
+ /** @todo Be sure to revise this when implementing offloading support! */
+ vboxNetLwfWinIndicateOffload(pModuleCtx, pModuleCtx->pDisabledOffloadConfig);
+ Log(("vboxNetLwfWinToggleOffloading: set offloading off\n"));
+ }
+ else
+ {
+ /* The filter is inactive -- restore offloading configuration. */
+ if (pModuleCtx->fOffloadConfigValid)
+ {
+ vboxNetLwfWinIndicateOffload(pModuleCtx, pModuleCtx->pSavedOffloadConfig);
+ Log(("vboxNetLwfWinToggleOffloading: restored offloading config\n"));
+ }
+ else
+ DbgPrint("VBoxNetLwf: no saved offload config to restore for %s\n", pModuleCtx->szMiniportName);
+ }
+}
+
+
+void vboxNetFltPortOsSetActive(PVBOXNETFLTINS pThis, bool fActive)
+{
+ PVBOXNETLWF_MODULE pModuleCtx = (PVBOXNETLWF_MODULE)pThis->u.s.WinIf.hModuleCtx;
+ LogFlow(("==>vboxNetFltPortOsSetActive: instance=%p module=%p fActive=%RTbool\n", pThis, pModuleCtx, fActive));
+ if (!pModuleCtx)
+ {
+ LogFlow(("<==vboxNetFltPortOsSetActive: pModuleCtx is null\n"));
+ return;
+ }
+
+ NDIS_STATUS Status = STATUS_SUCCESS;
+ bool fOldActive = ASMAtomicXchgBool(&pModuleCtx->fActive, fActive);
+ if (fOldActive != fActive)
+ {
+ NdisQueueIoWorkItem(pModuleCtx->hWorkItem, vboxNetLwfWinToggleOffloading, pModuleCtx);
+ Status = vboxNetLwfWinSetPacketFilter(pModuleCtx, fActive);
+ LogFlow(("<==vboxNetFltPortOsSetActive: vboxNetLwfWinSetPacketFilter() returned 0x%x\n", Status));
+ }
+ else
+ LogFlow(("<==vboxNetFltPortOsSetActive: no change, remain %sactive\n", fActive ? "":"in"));
+}
+
+int vboxNetFltOsDisconnectIt(PVBOXNETFLTINS pThis)
+{
+ RT_NOREF1(pThis);
+ LogFlow(("==>vboxNetFltOsDisconnectIt: instance=%p\n", pThis));
+ LogFlow(("<==vboxNetFltOsDisconnectIt: return 0\n"));
+ return VINF_SUCCESS;
+}
+
+int vboxNetFltOsConnectIt(PVBOXNETFLTINS pThis)
+{
+ RT_NOREF1(pThis);
+ LogFlow(("==>vboxNetFltOsConnectIt: instance=%p\n", pThis));
+ LogFlow(("<==vboxNetFltOsConnectIt: return 0\n"));
+ return VINF_SUCCESS;
+}
+
+/*
+ * Uncommenting the following line produces debug log messages on IP address changes,
+ * including wired interfaces. No actual calls to a switch port are made. This is for
+ * debug purposes only!
+ * #define VBOXNETLWFWIN_DEBUGIPADDRNOTIF 1
+ */
+static void __stdcall vboxNetLwfWinIpAddrChangeCallback(IN PVOID pvCtx,
+ IN PMIB_UNICASTIPADDRESS_ROW pRow,
+ IN MIB_NOTIFICATION_TYPE enmNotifType)
+{
+ PVBOXNETFLTINS pThis = (PVBOXNETFLTINS)pvCtx;
+
+ /* We are only interested in add or remove notifications. */
+ bool fAdded;
+ if (enmNotifType == MibAddInstance)
+ fAdded = true;
+ else if (enmNotifType == MibDeleteInstance)
+ fAdded = false;
+ else
+ return;
+
+ if ( pRow
+#ifndef VBOXNETLWFWIN_DEBUGIPADDRNOTIF
+ && pThis->pSwitchPort->pfnNotifyHostAddress
+#endif /* !VBOXNETLWFWIN_DEBUGIPADDRNOTIF */
+ )
+ {
+ switch (pRow->Address.si_family)
+ {
+ case AF_INET:
+ if ( IN4_IS_ADDR_LINKLOCAL(&pRow->Address.Ipv4.sin_addr)
+ || pRow->Address.Ipv4.sin_addr.s_addr == IN4ADDR_LOOPBACK)
+ {
+ Log(("vboxNetLwfWinIpAddrChangeCallback: ignoring %s address (%RTnaipv4)\n",
+ pRow->Address.Ipv4.sin_addr.s_addr == IN4ADDR_LOOPBACK ? "loopback" : "link-local",
+ pRow->Address.Ipv4.sin_addr));
+ break;
+ }
+ Log(("vboxNetLwfWinIpAddrChangeCallback: %s IPv4 addr=%RTnaipv4 on luid=(%u,%u)\n",
+ fAdded ? "add" : "remove", pRow->Address.Ipv4.sin_addr,
+ pRow->InterfaceLuid.Info.IfType, pRow->InterfaceLuid.Info.NetLuidIndex));
+#ifndef VBOXNETLWFWIN_DEBUGIPADDRNOTIF
+ pThis->pSwitchPort->pfnNotifyHostAddress(pThis->pSwitchPort, fAdded, kIntNetAddrType_IPv4,
+ &pRow->Address.Ipv4.sin_addr);
+#endif /* !VBOXNETLWFWIN_DEBUGIPADDRNOTIF */
+ break;
+ case AF_INET6:
+ if (Ipv6AddressScope(pRow->Address.Ipv6.sin6_addr.u.Byte) <= ScopeLevelLink)
+ {
+ Log(("vboxNetLwfWinIpAddrChangeCallback: ignoring link-local address (%RTnaipv6)\n",
+ &pRow->Address.Ipv6.sin6_addr));
+ break;
+ }
+ Log(("vboxNetLwfWinIpAddrChangeCallback: %s IPv6 addr=%RTnaipv6 scope=%d luid=(%u,%u)\n",
+ fAdded ? "add" : "remove", &pRow->Address.Ipv6.sin6_addr,
+ Ipv6AddressScope(pRow->Address.Ipv6.sin6_addr.u.Byte),
+ pRow->InterfaceLuid.Info.IfType, pRow->InterfaceLuid.Info.NetLuidIndex));
+#ifndef VBOXNETLWFWIN_DEBUGIPADDRNOTIF
+ pThis->pSwitchPort->pfnNotifyHostAddress(pThis->pSwitchPort, fAdded, kIntNetAddrType_IPv6,
+ &pRow->Address.Ipv6.sin6_addr);
+#endif /* !VBOXNETLWFWIN_DEBUGIPADDRNOTIF */
+ break;
+ }
+ }
+ else
+ Log(("vboxNetLwfWinIpAddrChangeCallback: pRow=%p pfnNotifyHostAddress=%p\n",
+ pRow, pThis->pSwitchPort->pfnNotifyHostAddress));
+}
+
+void vboxNetLwfWinRegisterIpAddrNotifier(PVBOXNETFLTINS pThis)
+{
+ LogFlow(("==>vboxNetLwfWinRegisterIpAddrNotifier: instance=%p\n", pThis));
+ if ( pThis->pSwitchPort
+#ifndef VBOXNETLWFWIN_DEBUGIPADDRNOTIF
+ && pThis->pSwitchPort->pfnNotifyHostAddress
+#endif /* !VBOXNETLWFWIN_DEBUGIPADDRNOTIF */
+ )
+ {
+ NETIO_STATUS Status;
+ /* First we need to go over all host IP addresses and add them via pfnNotifyHostAddress. */
+ PMIB_UNICASTIPADDRESS_TABLE HostIpAddresses = NULL;
+ Status = GetUnicastIpAddressTable(AF_UNSPEC, &HostIpAddresses);
+ if (NETIO_SUCCESS(Status))
+ {
+ for (unsigned i = 0; i < HostIpAddresses->NumEntries; i++)
+ vboxNetLwfWinIpAddrChangeCallback(pThis, &HostIpAddresses->Table[i], MibAddInstance);
+ }
+ else
+ LogError(("vboxNetLwfWinRegisterIpAddrNotifier: GetUnicastIpAddressTable failed with %x\n", Status));
+ /* Now we can register a callback function to keep track of address changes. */
+ Status = NotifyUnicastIpAddressChange(AF_UNSPEC, vboxNetLwfWinIpAddrChangeCallback,
+ pThis, false, &pThis->u.s.WinIf.hNotifier);
+ if (NETIO_SUCCESS(Status))
+ Log(("vboxNetLwfWinRegisterIpAddrNotifier: notifier=%p\n", pThis->u.s.WinIf.hNotifier));
+ else
+ LogError(("vboxNetLwfWinRegisterIpAddrNotifier: NotifyUnicastIpAddressChange failed with %x\n", Status));
+ }
+ else
+ pThis->u.s.WinIf.hNotifier = NULL;
+ LogFlow(("<==vboxNetLwfWinRegisterIpAddrNotifier\n"));
+}
+
+void vboxNetLwfWinUnregisterIpAddrNotifier(PVBOXNETFLTINS pThis)
+{
+ Log(("vboxNetLwfWinUnregisterIpAddrNotifier: notifier=%p\n", pThis->u.s.WinIf.hNotifier));
+ if (pThis->u.s.WinIf.hNotifier)
+ CancelMibChangeNotify2(pThis->u.s.WinIf.hNotifier);
+}
+
+void vboxNetFltOsDeleteInstance(PVBOXNETFLTINS pThis)
+{
+ PVBOXNETLWF_MODULE pModuleCtx = (PVBOXNETLWF_MODULE)pThis->u.s.WinIf.hModuleCtx;
+ LogFlow(("==>vboxNetFltOsDeleteInstance: instance=%p module=%p\n", pThis, pModuleCtx));
+ /* Cancel IP address change notifications */
+ vboxNetLwfWinUnregisterIpAddrNotifier(pThis);
+ /* Technically it is possible that the module has already been gone by now. */
+ if (pModuleCtx)
+ {
+ Assert(!pModuleCtx->fActive); /* Deactivation ensures bypass mode */
+ pModuleCtx->pNetFlt = NULL;
+ pThis->u.s.WinIf.hModuleCtx = NULL;
+ }
+ LogFlow(("<==vboxNetFltOsDeleteInstance\n"));
+}
+
+static void vboxNetLwfWinReportCapabilities(PVBOXNETFLTINS pThis, PVBOXNETLWF_MODULE pModuleCtx)
+{
+ if (pThis->pSwitchPort
+ && vboxNetFltTryRetainBusyNotDisconnected(pThis))
+ {
+ pThis->pSwitchPort->pfnReportMacAddress(pThis->pSwitchPort, &pModuleCtx->MacAddr);
+ pThis->pSwitchPort->pfnReportPromiscuousMode(pThis->pSwitchPort,
+ vboxNetLwfWinIsPromiscuous(pModuleCtx));
+ pThis->pSwitchPort->pfnReportGsoCapabilities(pThis->pSwitchPort, 0,
+ INTNETTRUNKDIR_WIRE | INTNETTRUNKDIR_HOST);
+ pThis->pSwitchPort->pfnReportNoPreemptDsts(pThis->pSwitchPort, 0 /* none */);
+ vboxNetFltRelease(pThis, true /*fBusy*/);
+ }
+}
+
+int vboxNetFltOsInitInstance(PVBOXNETFLTINS pThis, void *pvContext)
+{
+ RT_NOREF1(pvContext);
+ LogFlow(("==>vboxNetFltOsInitInstance: instance=%p context=%p\n", pThis, pvContext));
+ AssertReturn(pThis, VERR_INVALID_PARAMETER);
+ Log(("vboxNetFltOsInitInstance: trunk name=%s\n", pThis->szName));
+ NdisAcquireSpinLock(&g_VBoxNetLwfGlobals.Lock);
+ PVBOXNETLWF_MODULE pModuleCtx;
+ RTListForEach(&g_VBoxNetLwfGlobals.listModules, pModuleCtx, VBOXNETLWF_MODULE, node)
+ {
+ DbgPrint("vboxNetFltOsInitInstance: evaluating module, name=%s\n", pModuleCtx->szMiniportName);
+ if (!RTStrICmp(pThis->szName, pModuleCtx->szMiniportName))
+ {
+ NdisReleaseSpinLock(&g_VBoxNetLwfGlobals.Lock);
+ Log(("vboxNetFltOsInitInstance: found matching module, name=%s\n", pThis->szName));
+ pThis->u.s.WinIf.hModuleCtx = pModuleCtx;
+ pModuleCtx->pNetFlt = pThis;
+ vboxNetLwfWinReportCapabilities(pThis, pModuleCtx);
+ vboxNetLwfWinRegisterIpAddrNotifier(pThis);
+ LogFlow(("<==vboxNetFltOsInitInstance: return 0\n"));
+ return VINF_SUCCESS;
+ }
+ }
+ NdisReleaseSpinLock(&g_VBoxNetLwfGlobals.Lock);
+ // Internal network code will try to reconnect periodically, we should not spam in event log
+ //vboxNetLwfLogErrorEvent(IO_ERR_INTERNAL_ERROR, STATUS_SUCCESS, 6);
+ LogFlow(("<==vboxNetFltOsInitInstance: return VERR_INTNET_FLT_IF_NOT_FOUND\n"));
+ return VERR_INTNET_FLT_IF_NOT_FOUND;
+}
+
+int vboxNetFltOsPreInitInstance(PVBOXNETFLTINS pThis)
+{
+ LogFlow(("==>vboxNetFltOsPreInitInstance: instance=%p\n", pThis));
+ pThis->u.s.WinIf.hModuleCtx = 0;
+ pThis->u.s.WinIf.hNotifier = NULL;
+ LogFlow(("<==vboxNetFltOsPreInitInstance: return 0\n"));
+ return VINF_SUCCESS;
+}
+
+void vboxNetFltPortOsNotifyMacAddress(PVBOXNETFLTINS pThis, void *pvIfData, PCRTMAC pMac)
+{
+ RT_NOREF3(pThis, pvIfData, pMac);
+ LogFlow(("==>vboxNetFltPortOsNotifyMacAddress: instance=%p data=%p mac=%RTmac\n", pThis, pvIfData, pMac));
+ LogFlow(("<==vboxNetFltPortOsNotifyMacAddress\n"));
+}
+
+int vboxNetFltPortOsConnectInterface(PVBOXNETFLTINS pThis, void *pvIf, void **ppvIfData)
+{
+ RT_NOREF3(pThis, pvIf, ppvIfData);
+ LogFlow(("==>vboxNetFltPortOsConnectInterface: instance=%p if=%p data=%p\n", pThis, pvIf, ppvIfData));
+ LogFlow(("<==vboxNetFltPortOsConnectInterface: return 0\n"));
+ /* Nothing to do */
+ return VINF_SUCCESS;
+}
+
+int vboxNetFltPortOsDisconnectInterface(PVBOXNETFLTINS pThis, void *pvIfData)
+{
+ RT_NOREF2(pThis, pvIfData);
+ LogFlow(("==>vboxNetFltPortOsDisconnectInterface: instance=%p data=%p\n", pThis, pvIfData));
+ LogFlow(("<==vboxNetFltPortOsDisconnectInterface: return 0\n"));
+ /* Nothing to do */
+ return VINF_SUCCESS;
+}
+
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetLwf-win.h b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetLwf-win.h
new file mode 100644
index 00000000..1a0a9e7e
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetLwf-win.h
@@ -0,0 +1,55 @@
+/* $Id: VBoxNetLwf-win.h $ */
+/** @file
+ * VBoxNetLwf-win.h - Bridged 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 <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_SRC_VBoxNetFlt_win_drv_VBoxNetLwf_win_h
+#define VBOX_INCLUDED_SRC_VBoxNetFlt_win_drv_VBoxNetLwf_win_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#define VBOXNETLWF_VERSION_NDIS_MAJOR 6
+#define VBOXNETLWF_VERSION_NDIS_MINOR 0
+
+#define VBOXNETLWF_NAME_FRIENDLY L"VirtualBox NDIS Light-Weight Filter"
+#define VBOXNETLWF_NAME_UNIQUE L"{7af6b074-048d-4444-bfce-1ecc8bc5cb76}"
+#define VBOXNETLWF_NAME_SERVICE L"VBoxNetLwf"
+
+#define VBOXNETLWF_NAME_LINK L"\\DosDevices\\Global\\VBoxNetLwf"
+#define VBOXNETLWF_NAME_DEVICE L"\\Device\\VBoxNetLwf"
+
+#define VBOXNETLWF_MEM_TAG 'FLBV'
+#define VBOXNETLWF_REQ_ID 'fLBV'
+
+#endif /* !VBOX_INCLUDED_SRC_VBoxNetFlt_win_drv_VBoxNetLwf_win_h */
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetLwf.inf b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetLwf.inf
new file mode 100644
index 00000000..f5d9cdca
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetLwf.inf
@@ -0,0 +1,120 @@
+; $Id: VBoxNetLwf.inf $
+; @file
+; VBoxNetLwf.inf - VirtualBox Bridged Networking Driver inf file
+;
+
+;
+; 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 <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+[Version]
+Signature = "$Windows NT$"
+;cat CatalogFile = VBoxNetLwf.cat
+Class = NetService
+ClassGUID = {4D36E974-E325-11CE-BFC1-08002BE10318}
+Provider = %ORACLE%
+;edit-DriverVer=10/23/2014,1.0.1.0
+
+
+[Manufacturer]
+%ORACLE% = VBoxNetLwf@COMMA-NT-ARCH@
+
+[ControlFlags]
+
+[VBoxNetLwf@DOT-NT-ARCH@]
+%VBoxNetLwf_Desc% = VBoxNetLwf.ndi, oracle_VBoxNetLwf
+
+[VBoxNetLwf.ndi]
+AddReg = VBoxNetLwf.ndi.AddReg, VBoxNetLwf.AddReg
+Characteristics = 0x40000 ; NCF_LW_FILTER
+CopyFiles = VBoxNetLwf.Files.Sys
+NetCfgInstanceId = "{7af6b074-048d-4444-bfce-1ecc8bc5cb76}"
+
+[VBoxNetLwf.ndi.Remove.Services]
+DelService = VBoxNetLwf,0x200 ; Stop the service before uninstalling
+
+[VBoxNetLwf.ndi.Services]
+AddService = VBoxNetLwf,, VBoxNetLwf.AddService, VBoxNetLwf.AddEventLog
+
+[VBoxNetLwf.AddService]
+DisplayName = %VBoxNetLwfService_Desc%
+ServiceType = 1 ;SERVICE_KERNEL_DRIVER
+StartType = 1 ;SERVICE_SYSTEM_START
+ErrorControl = 1 ;SERVICE_ERROR_NORMAL
+ServiceBinary = %12%\VBoxNetLwf.sys
+LoadOrderGroup = NDIS
+AddReg = VBoxNetLwf.AddService.AddReg
+
+[VBoxNetLwf.AddService.AddReg]
+
+[VBoxNetLwf.AddEventLog]
+AddReg = VBoxNetLwf.AddEventLog.AddReg
+
+[VBoxNetLwf.AddEventLog.AddReg]
+HKR,,EventMessageFile,0x00020000,"%%SystemRoot%%\System32\IoLogMsg.dll"
+HKR,,TypesSupported,0x00010001,7
+
+
+[SourceDisksNames]
+1=%DiskDescription%,"",,
+
+[SourceDisksFiles]
+VBoxNetLwf.sys=1
+
+[DestinationDirs]
+DefaultDestDir = 12
+VBoxNetLwf.Files.Sys = 12 ; %windir%\System32\drivers
+
+[VBoxNetLwf.Files.Sys]
+VBoxNetLwf.sys,,,2
+
+
+[VBoxNetLwf.ndi.AddReg]
+HKR, Ndi, HelpText, , %VBoxNetLwf_HELP%
+;HKR, Ndi, ClsID, 0, {f374d1a0-bf08-4bdc-9cb2-c15ddaeef955}
+;HKR, Ndi, ComponentDll, , VBoxNetLwfNobj.dll
+HKR, Ndi, FilterClass, , compression
+HKR, Ndi, FilterType, 0x10001, 0x2
+HKR, Ndi, FilterRunType,0x10001, 2 ; OPTIONAL, to prevent unbinding of protocol drivers
+HKR, Ndi, Service, , VBoxNetLwf
+HKR, Ndi, CoServices, 0x10000, VBoxNetLwf
+HKR, Ndi\Interfaces, UpperRange, , noupper
+HKR, Ndi\Interfaces, LowerRange, , nolower
+HKR, Ndi\Interfaces, FilterMediaTypes, , ethernet
+
+[VBoxNetLwf.AddReg]
+;HKR, Parameters, Param1, 0, 4
+
+[Strings]
+ORACLE = "Oracle Corporation"
+DiskDescription = "VirtualBox NDIS6 Bridged Networking Driver"
+VBoxNetLwf_Desc = "VirtualBox NDIS6 Bridged Networking Driver"
+VBoxNetLwf_HELP = "VirtualBox NDIS6 Bridged Networking Driver"
+VBoxNetLwfService_Desc = "VirtualBox NDIS6 Bridged Networking Service"
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/Makefile.kup b/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/Makefile.kup
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobj.cpp b/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobj.cpp
new file mode 100644
index 00000000..a5cf004c
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobj.cpp
@@ -0,0 +1,747 @@
+/* $Id: VBoxNetFltNobj.cpp $ */
+/** @file
+ * VBoxNetFltNobj.cpp - Notify Object for Bridged Networking Driver.
+ *
+ * Used to filter Bridged Networking Driver bindings
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "VBoxNetFltNobj.h"
+#include <iprt/win/ntddndis.h>
+#include <iprt/win/windows.h>
+#include <winreg.h>
+#include <Olectl.h>
+
+#include <VBoxNetFltNobjT_i.c>
+
+#include <iprt/assert.h>
+#include <iprt/utf16.h>
+#include <iprt/string.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+//# define VBOXNETFLTNOTIFY_DEBUG_BIND
+
+#ifdef DEBUG
+# define NonStandardAssert(a) Assert(a)
+# define NonStandardAssertBreakpoint() AssertFailed()
+#else
+# define NonStandardAssert(a) do{}while (0)
+# define NonStandardAssertBreakpoint() do{}while (0)
+#endif
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static HMODULE g_hModSelf = (HMODULE)~(uintptr_t)0;
+
+
+VBoxNetFltNobj::VBoxNetFltNobj()
+ : mpNetCfg(NULL)
+ , mpNetCfgComponent(NULL)
+ , mbInstalling(FALSE)
+{
+}
+
+VBoxNetFltNobj::~VBoxNetFltNobj()
+{
+ cleanup();
+}
+
+void VBoxNetFltNobj::cleanup()
+{
+ if (mpNetCfg)
+ {
+ mpNetCfg->Release();
+ mpNetCfg = NULL;
+ }
+
+ if (mpNetCfgComponent)
+ {
+ mpNetCfgComponent->Release();
+ mpNetCfgComponent = NULL;
+ }
+}
+
+void VBoxNetFltNobj::init(IN INetCfgComponent *pNetCfgComponent, IN INetCfg *pNetCfg, IN BOOL bInstalling)
+{
+ cleanup();
+
+ NonStandardAssert(pNetCfg);
+ NonStandardAssert(pNetCfgComponent);
+ if (pNetCfg)
+ {
+ pNetCfg->AddRef();
+ mpNetCfg = pNetCfg;
+ }
+
+ if (pNetCfgComponent)
+ {
+ pNetCfgComponent->AddRef();
+ mpNetCfgComponent = pNetCfgComponent;
+ }
+
+ mbInstalling = bInstalling;
+}
+
+/* INetCfgComponentControl methods */
+STDMETHODIMP VBoxNetFltNobj::Initialize(IN INetCfgComponent *pNetCfgComponent, IN INetCfg *pNetCfg, IN BOOL bInstalling)
+{
+ init(pNetCfgComponent, pNetCfg, bInstalling);
+ return S_OK;
+}
+
+STDMETHODIMP VBoxNetFltNobj::ApplyRegistryChanges()
+{
+ return S_OK;
+}
+
+STDMETHODIMP VBoxNetFltNobj::ApplyPnpChanges(IN INetCfgPnpReconfigCallback *pCallback)
+{
+ RT_NOREF1(pCallback);
+ return S_OK;
+}
+
+STDMETHODIMP VBoxNetFltNobj::CancelChanges()
+{
+ return S_OK;
+}
+
+static HRESULT vboxNetFltWinQueryInstanceKey(IN INetCfgComponent *pComponent, OUT PHKEY phKey)
+{
+ LPWSTR pwszPnpId;
+ HRESULT hrc = pComponent->GetPnpDevNodeId(&pwszPnpId);
+ if (hrc == S_OK)
+ {
+ WCHAR wszKeyName[MAX_PATH];
+ RTUtf16Copy(wszKeyName, MAX_PATH, L"SYSTEM\\CurrentControlSet\\Enum\\");
+ int rc = RTUtf16Cat(wszKeyName, MAX_PATH, pwszPnpId);
+ if (RT_SUCCESS(rc))
+ {
+ LSTATUS lrc = RegOpenKeyExW(HKEY_LOCAL_MACHINE, wszKeyName, 0 /*ulOptions*/, KEY_READ, phKey);
+ if (lrc != ERROR_SUCCESS)
+ {
+ hrc = HRESULT_FROM_WIN32(lrc);
+ NonStandardAssertBreakpoint();
+ }
+ }
+ else
+ AssertRCStmt(rc, hrc = ERROR_BUFFER_OVERFLOW);
+
+ CoTaskMemFree(pwszPnpId);
+ }
+ else
+ NonStandardAssertBreakpoint();
+ return hrc;
+}
+
+static HRESULT vboxNetFltWinQueryDriverKey(IN HKEY InstanceKey, OUT PHKEY phKey)
+{
+ HRESULT hrc = S_OK;
+
+ WCHAR wszValue[MAX_PATH];
+ DWORD cbValue = sizeof(wszValue) - sizeof(WCHAR);
+ DWORD dwType = REG_SZ;
+ LSTATUS lrc = RegQueryValueExW(InstanceKey, L"Driver", NULL /*lpReserved*/, &dwType, (LPBYTE)wszValue, &cbValue);
+ if (lrc == ERROR_SUCCESS)
+ {
+ if (dwType == REG_SZ)
+ {
+ wszValue[RT_ELEMENTS(wszValue) - 1] = '\0'; /* registry strings does not need to be zero terminated. */
+
+ WCHAR wszKeyName[MAX_PATH];
+ RTUtf16Copy(wszKeyName, MAX_PATH, L"SYSTEM\\CurrentControlSet\\Control\\Class\\");
+ int rc = RTUtf16Cat(wszKeyName, MAX_PATH, wszValue);
+ if (RT_SUCCESS(rc))
+ {
+ lrc = RegOpenKeyExW(HKEY_LOCAL_MACHINE, wszKeyName, 0 /*ulOptions*/, KEY_READ, phKey);
+ if (lrc != ERROR_SUCCESS)
+ {
+ hrc = HRESULT_FROM_WIN32(lrc);
+ NonStandardAssertBreakpoint();
+ }
+ }
+ else
+ AssertRCStmt(rc, hrc = ERROR_BUFFER_OVERFLOW);
+ }
+ else
+ {
+ hrc = HRESULT_FROM_WIN32(ERROR_DATATYPE_MISMATCH);
+ NonStandardAssertBreakpoint();
+ }
+ }
+ else
+ {
+ hrc = HRESULT_FROM_WIN32(lrc);
+ NonStandardAssertBreakpoint();
+ }
+
+ return hrc;
+}
+
+static HRESULT vboxNetFltWinQueryDriverKey(IN INetCfgComponent *pComponent, OUT PHKEY phKey)
+{
+ HKEY hKeyInstance = NULL;
+ HRESULT hrc = vboxNetFltWinQueryInstanceKey(pComponent, &hKeyInstance);
+ if (hrc == S_OK)
+ {
+ hrc = vboxNetFltWinQueryDriverKey(hKeyInstance, phKey);
+ if (hrc != S_OK)
+ NonStandardAssertBreakpoint();
+ RegCloseKey(hKeyInstance);
+ }
+ else
+ NonStandardAssertBreakpoint();
+ return hrc;
+}
+
+static HRESULT vboxNetFltWinNotifyCheckNetAdp(IN INetCfgComponent *pComponent, OUT bool *pfShouldBind)
+{
+ *pfShouldBind = false;
+
+ LPWSTR pwszDevId = NULL;
+ HRESULT hrc = pComponent->GetId(&pwszDevId);
+ if (hrc == S_OK)
+ {
+ /** @todo r=bird: This was _wcsnicmp(pwszDevId, L"sun_VBoxNetAdp", sizeof(L"sun_VBoxNetAdp")/2))
+ * which includes the terminator, so it translates to a full compare. Goes way back. */
+ if (RTUtf16ICmpAscii(pwszDevId, "sun_VBoxNetAdp") == 0)
+ *pfShouldBind = false;
+ else
+ hrc = S_FALSE;
+ CoTaskMemFree(pwszDevId);
+ }
+ else
+ NonStandardAssertBreakpoint();
+
+ return hrc;
+}
+
+static HRESULT vboxNetFltWinNotifyCheckMsLoop(IN INetCfgComponent *pComponent, OUT bool *pfShouldBind)
+{
+ *pfShouldBind = false;
+
+ LPWSTR pwszDevId = NULL;
+ HRESULT hrc = pComponent->GetId(&pwszDevId);
+ if (hrc == S_OK)
+ {
+ /** @todo r=bird: This was _wcsnicmp(pwszDevId, L"*msloop", sizeof(L"*msloop")/2)
+ * which includes the terminator, making it a full compare. Goes way back. */
+ if (RTUtf16ICmpAscii(pwszDevId, "*msloop") == 0)
+ {
+ /* we need to detect the medium the adapter is presenting
+ * to do that we could examine in the registry the *msloop params */
+ HKEY hKeyDriver;
+ hrc = vboxNetFltWinQueryDriverKey(pComponent, &hKeyDriver);
+ if (hrc == S_OK)
+ {
+ WCHAR wszValue[64]; /* 2 should be enough actually, paranoid check for extra spaces */
+ DWORD cbValue = sizeof(wszValue) - sizeof(WCHAR);
+ DWORD dwType = REG_SZ;
+ LSTATUS lrc = RegQueryValueExW(hKeyDriver, L"Medium", NULL /*lpReserved*/, &dwType, (LPBYTE)wszValue, &cbValue);
+ if (lrc == ERROR_SUCCESS)
+ {
+ if (dwType == REG_SZ)
+ {
+ wszValue[RT_ELEMENTS(wszValue) - 1] = '\0';
+
+ char szUtf8[256];
+ char *pszUtf8 = szUtf8;
+ RTUtf16ToUtf8Ex(wszValue, RTSTR_MAX, &pszUtf8, sizeof(szUtf8), NULL);
+ pszUtf8 = RTStrStrip(pszUtf8);
+
+ uint64_t uValue = 0;
+ int rc = RTStrToUInt64Ex(pszUtf8, NULL, 0, &uValue);
+ if (RT_SUCCESS(rc))
+ {
+ if (uValue == 0) /* 0 is Ethernet */
+ *pfShouldBind = true;
+ else
+ *pfShouldBind = false;
+ }
+ else
+ {
+ NonStandardAssertBreakpoint();
+ *pfShouldBind = true;
+ }
+ }
+ else
+ NonStandardAssertBreakpoint();
+ }
+ else
+ {
+ /** @todo we should check the default medium in HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002bE10318}\<driver_id>\Ndi\Params\Medium, REG_SZ "Default" value */
+ NonStandardAssertBreakpoint();
+ *pfShouldBind = true;
+ }
+
+ RegCloseKey(hKeyDriver);
+ }
+ else
+ NonStandardAssertBreakpoint();
+ }
+ else
+ hrc = S_FALSE;
+ CoTaskMemFree(pwszDevId);
+ }
+ else
+ NonStandardAssertBreakpoint();
+ return hrc;
+}
+
+static HRESULT vboxNetFltWinNotifyCheckLowerRange(IN INetCfgComponent *pComponent, OUT bool *pfShouldBind)
+{
+ *pfShouldBind = false;
+
+ HKEY hKeyDriver = NULL;
+ HRESULT hrc = vboxNetFltWinQueryDriverKey(pComponent, &hKeyDriver);
+ if (hrc == S_OK)
+ {
+ HKEY hKeyInterfaces = NULL;
+ LSTATUS lrc = RegOpenKeyExW(hKeyDriver, L"Ndi\\Interfaces", 0 /*ulOptions*/, KEY_READ, &hKeyInterfaces);
+ if (lrc == ERROR_SUCCESS)
+ {
+ WCHAR wszValue[MAX_PATH];
+ DWORD cbValue = sizeof(wszValue) - sizeof(WCHAR);
+ DWORD dwType = REG_SZ;
+ lrc = RegQueryValueExW(hKeyInterfaces, L"LowerRange", NULL /*lpReserved*/, &dwType, (LPBYTE)wszValue, &cbValue);
+ if (lrc == ERROR_SUCCESS)
+ {
+ if (dwType == REG_SZ)
+ {
+ if (RTUtf16FindAscii(wszValue, "ethernet") >= 0 || RTUtf16FindAscii(wszValue, "wan") >= 0)
+ *pfShouldBind = true;
+ else
+ *pfShouldBind = false;
+ }
+ }
+ else
+ {
+ /* do not set err status to it */
+ *pfShouldBind = false;
+ NonStandardAssertBreakpoint();
+ }
+
+ RegCloseKey(hKeyInterfaces);
+ }
+ else
+ {
+ hrc = HRESULT_FROM_WIN32(lrc);
+ NonStandardAssertBreakpoint();
+ }
+
+ RegCloseKey(hKeyDriver);
+ }
+ else
+ NonStandardAssertBreakpoint();
+ return hrc;
+}
+
+static HRESULT vboxNetFltWinNotifyShouldBind(IN INetCfgComponent *pComponent, OUT bool *pfShouldBind)
+{
+ *pfShouldBind = false;
+
+ /* filter out only physical adapters */
+ DWORD fCharacteristics = 0;
+ HRESULT hrc = pComponent->GetCharacteristics(&fCharacteristics);
+ if (hrc != S_OK)
+ {
+ NonStandardAssertBreakpoint();
+ return hrc;
+ }
+
+ /* we are not binding to hidden adapters */
+ if (fCharacteristics & NCF_HIDDEN)
+ return S_OK;
+
+ hrc = vboxNetFltWinNotifyCheckMsLoop(pComponent, pfShouldBind);
+ if ( hrc == S_OK /* this is a loopback adapter, the pfShouldBind already contains the result */
+ || hrc != S_FALSE /* error occurred */)
+ return hrc;
+
+ hrc = vboxNetFltWinNotifyCheckNetAdp(pComponent, pfShouldBind);
+ if ( hrc == S_OK /* this is a VBoxNetAdp adapter, the pfShouldBind already contains the result */
+ || hrc != S_FALSE /* error occurred */)
+ return hrc;
+
+ //if (!(fCharacteristics & NCF_PHYSICAL))
+ //{
+ // *pfShouldBind = false; /* we are binding to physical adapters only */
+ // return S_OK;
+ //}
+
+ return vboxNetFltWinNotifyCheckLowerRange(pComponent, pfShouldBind);
+}
+
+
+static HRESULT vboxNetFltWinNotifyShouldBind(IN INetCfgBindingInterface *pIf, OUT bool *pfShouldBind)
+{
+ INetCfgComponent *pAdapterComponent = NULL;
+ HRESULT hrc = pIf->GetLowerComponent(&pAdapterComponent);
+ if (hrc == S_OK)
+ {
+ hrc = vboxNetFltWinNotifyShouldBind(pAdapterComponent, pfShouldBind);
+
+ pAdapterComponent->Release();
+ }
+ else
+ {
+ NonStandardAssertBreakpoint();
+ *pfShouldBind = false;
+ }
+ return hrc;
+}
+
+static HRESULT vboxNetFltWinNotifyShouldBind(IN INetCfgBindingPath *pPath, OUT bool *pfShouldBind)
+{
+ *pfShouldBind = false;
+
+ IEnumNetCfgBindingInterface *pIEnumBinding = NULL;
+ HRESULT hrc = pPath->EnumBindingInterfaces(&pIEnumBinding);
+ if (hrc == S_OK)
+ {
+ hrc = pIEnumBinding->Reset();
+ if (hrc == S_OK)
+ {
+ for (;;)
+ {
+ ULONG uCount = 0;
+ INetCfgBindingInterface *pIBinding = NULL;
+ hrc = pIEnumBinding->Next(1, &pIBinding, &uCount);
+ if (hrc == S_OK)
+ {
+ hrc = vboxNetFltWinNotifyShouldBind(pIBinding, pfShouldBind);
+ pIBinding->Release();
+
+ if (hrc != S_OK)
+ break; /* break on failure. */
+ if (!*pfShouldBind)
+ break;
+ }
+ else if (hrc == S_FALSE)
+ {
+ /* no more elements */
+ hrc = S_OK;
+ break;
+ }
+ else
+ {
+ NonStandardAssertBreakpoint();
+ /* break on falure */
+ break;
+ }
+ }
+ }
+ else
+ NonStandardAssertBreakpoint();
+
+ pIEnumBinding->Release();
+ }
+ else
+ NonStandardAssertBreakpoint();
+ return hrc;
+}
+
+static bool vboxNetFltWinNotifyShouldBind(IN INetCfgBindingPath *pPath)
+{
+#ifdef VBOXNETFLTNOTIFY_DEBUG_BIND
+ return VBOXNETFLTNOTIFY_DEBUG_BIND;
+#else
+ bool fShouldBind;
+ HRESULT hrc = vboxNetFltWinNotifyShouldBind(pPath, &fShouldBind);
+ if (hrc != S_OK)
+ fShouldBind = VBOXNETFLTNOTIFY_ONFAIL_BINDDEFAULT;
+
+ return fShouldBind;
+#endif
+}
+
+
+/* INetCfgComponentNotifyBinding methods */
+STDMETHODIMP VBoxNetFltNobj::NotifyBindingPath(IN DWORD dwChangeFlag, IN INetCfgBindingPath *pNetCfgBP)
+{
+ if (!(dwChangeFlag & NCN_ENABLE) || (dwChangeFlag & NCN_REMOVE) || vboxNetFltWinNotifyShouldBind(pNetCfgBP))
+ return S_OK;
+ return NETCFG_S_DISABLE_QUERY;
+}
+
+STDMETHODIMP VBoxNetFltNobj::QueryBindingPath(IN DWORD dwChangeFlag, IN INetCfgBindingPath *pNetCfgBP)
+{
+ RT_NOREF1(dwChangeFlag);
+ if (vboxNetFltWinNotifyShouldBind(pNetCfgBP))
+ return S_OK;
+ return NETCFG_S_DISABLE_QUERY;
+}
+
+
+static ATL::CComModule _Module;
+
+BEGIN_OBJECT_MAP(ObjectMap)
+ OBJECT_ENTRY(CLSID_VBoxNetFltNobj, VBoxNetFltNobj)
+END_OBJECT_MAP()
+
+
+extern "C"
+BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /*lpReserved*/)
+{
+ if (dwReason == DLL_PROCESS_ATTACH)
+ {
+ g_hModSelf = (HMODULE)hInstance;
+
+ _Module.Init(ObjectMap, hInstance);
+ DisableThreadLibraryCalls(hInstance);
+ }
+ else if (dwReason == DLL_PROCESS_DETACH)
+ {
+ _Module.Term();
+ }
+ return TRUE;
+}
+
+STDAPI DllCanUnloadNow(void)
+{
+ return _Module.GetLockCount() == 0 ? S_OK : S_FALSE;
+}
+
+STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv)
+{
+ return _Module.GetClassObject(rclsid, riid, ppv);
+}
+
+
+/*
+ * ATL::CComModule does not suport server registration/unregistration methods,
+ * so we need to do it manually. Since this is the only place we do registraton
+ * manually, we do it the quick-and-dirty way.
+ */
+
+#ifdef RT_EXCEPTIONS_ENABLED
+/* Someday we may want to log errors. */
+class AdHocRegError
+{
+public:
+ AdHocRegError(LSTATUS rc) { RT_NOREF1(rc); };
+};
+#endif
+
+/**
+ * A simple wrapper on Windows registry functions.
+ */
+class AdHocRegKey
+{
+public:
+ AdHocRegKey(HKEY hKey) : m_hKey(hKey) {};
+ AdHocRegKey(LPCWSTR pcwszName, HKEY hParent = HKEY_CLASSES_ROOT);
+ ~AdHocRegKey() { RegCloseKey(m_hKey); };
+
+ AdHocRegKey *create(LPCWSTR pcwszSubkey);
+ LSTATUS setValue(LPCWSTR pcwszName, LPCWSTR pcwszValue);
+ HKEY getKey(void) { return m_hKey; };
+private:
+ HKEY m_hKey;
+};
+
+AdHocRegKey::AdHocRegKey(LPCWSTR pcwszName, HKEY hParent) : m_hKey(NULL)
+{
+ LSTATUS rc = RegOpenKeyExW(hParent, pcwszName, 0, KEY_ALL_ACCESS, &m_hKey);
+ if (rc != ERROR_SUCCESS)
+#ifdef RT_EXCEPTIONS_ENABLED
+ throw AdHocRegError(rc);
+#else
+ m_hKey = NULL;
+#endif
+}
+
+AdHocRegKey *AdHocRegKey::create(LPCWSTR pcwszSubkey)
+{
+ HKEY hSubkey;
+ LSTATUS rc = RegCreateKeyExW(m_hKey, pcwszSubkey,
+ 0 /*Reserved*/, NULL /*pszClass*/, 0 /*fOptions*/,
+ KEY_ALL_ACCESS, NULL /*pSecAttr*/, &hSubkey, NULL /*pdwDisposition*/);
+ if (rc != ERROR_SUCCESS)
+#ifdef RT_EXCEPTIONS_ENABLED
+ throw AdHocRegError(rc);
+#else
+ return NULL;
+#endif
+ AdHocRegKey *pSubkey = new AdHocRegKey(hSubkey);
+ if (!pSubkey)
+ RegCloseKey(hSubkey);
+ return pSubkey;
+}
+
+LSTATUS AdHocRegKey::setValue(LPCWSTR pcwszName, LPCWSTR pcwszValue)
+{
+ LSTATUS rc = RegSetValueExW(m_hKey, pcwszName, 0, REG_SZ, (const BYTE *)pcwszValue,
+ (DWORD)((RTUtf16Len(pcwszValue) + 1) * sizeof(WCHAR)));
+#ifdef RT_EXCEPTIONS_ENABLED
+ if (rc != ERROR_SUCCESS)
+ throw AdHocRegError(rc);
+#endif
+ return rc;
+}
+
+/**
+ * Auxiliary class that facilitates automatic destruction of AdHocRegKey objects
+ * allocated in heap. No reference counting here!
+ */
+class AdHocRegKeyPtr
+{
+public:
+ AdHocRegKeyPtr(AdHocRegKey *pKey) : m_pKey(pKey) {};
+ ~AdHocRegKeyPtr()
+ {
+ if (m_pKey)
+ {
+ delete m_pKey;
+ m_pKey = NULL;
+ }
+ }
+
+ AdHocRegKey *create(LPCWSTR pcwszSubkey)
+ { return m_pKey ? m_pKey->create(pcwszSubkey) : NULL; };
+
+ LSTATUS setValue(LPCWSTR pcwszName, LPCWSTR pcwszValue)
+ { return m_pKey ? m_pKey->setValue(pcwszName, pcwszValue) : ERROR_INVALID_STATE; };
+
+private:
+ AdHocRegKey *m_pKey;
+ /* Prevent copying, since we do not support reference counting */
+ AdHocRegKeyPtr(const AdHocRegKeyPtr&);
+ AdHocRegKeyPtr& operator=(const AdHocRegKeyPtr&);
+};
+
+
+STDAPI DllRegisterServer(void)
+{
+ /* Get the path to the DLL we're running inside. */
+ WCHAR wszModule[MAX_PATH + 1];
+ UINT cwcModule = GetModuleFileNameW(g_hModSelf, wszModule, MAX_PATH);
+ if (cwcModule == 0 || cwcModule > MAX_PATH)
+ return SELFREG_E_CLASS;
+ wszModule[MAX_PATH] = '\0';
+
+ /*
+ * Create registry keys and values. When exceptions are disabled, we depend
+ * on setValue() to propagate fail key creation failures.
+ */
+#ifdef RT_EXCEPTIONS_ENABLED
+ try
+#endif
+ {
+ AdHocRegKey keyCLSID(L"CLSID");
+ AdHocRegKeyPtr pkeyNobjClass(keyCLSID.create(L"{f374d1a0-bf08-4bdc-9cb2-c15ddaeef955}"));
+ LSTATUS lrc = pkeyNobjClass.setValue(NULL, L"VirtualBox Bridged Networking Driver Notify Object v1.1");
+ if (lrc != ERROR_SUCCESS)
+ return SELFREG_E_CLASS;
+
+ AdHocRegKeyPtr pkeyNobjSrv(pkeyNobjClass.create(L"InProcServer32"));
+ lrc = pkeyNobjSrv.setValue(NULL, wszModule);
+ if (lrc != ERROR_SUCCESS)
+ return SELFREG_E_CLASS;
+ lrc = pkeyNobjSrv.setValue(L"ThreadingModel", L"Both");
+ if (lrc != ERROR_SUCCESS)
+ return SELFREG_E_CLASS;
+ }
+#ifdef RT_EXCEPTIONS_ENABLED
+ catch (AdHocRegError) { return SELFREG_E_CLASS; }
+#endif
+
+#ifdef RT_EXCEPTIONS_ENABLED
+ try
+#endif
+ {
+ AdHocRegKey keyTypeLib(L"TypeLib");
+ AdHocRegKeyPtr pkeyNobjLib(keyTypeLib.create(L"{2A0C94D1-40E1-439C-8FE8-24107CAB0840}\\1.1"));
+ LSTATUS lrc = pkeyNobjLib.setValue(NULL, L"VirtualBox Bridged Networking Driver Notify Object v1.1 Type Library");
+ if (lrc != ERROR_SUCCESS)
+ return SELFREG_E_TYPELIB;
+
+ AdHocRegKeyPtr pkeyNobjLib0(pkeyNobjLib.create(L"0\\win64"));
+ lrc = pkeyNobjLib0.setValue(NULL, wszModule);
+ if (lrc != ERROR_SUCCESS)
+ return SELFREG_E_TYPELIB;
+ AdHocRegKeyPtr pkeyNobjLibFlags(pkeyNobjLib.create(L"FLAGS"));
+ lrc = pkeyNobjLibFlags.setValue(NULL, L"0");
+ if (lrc != ERROR_SUCCESS)
+ return SELFREG_E_TYPELIB;
+
+ if (GetSystemDirectoryW(wszModule, MAX_PATH) == 0)
+ return SELFREG_E_TYPELIB;
+ AdHocRegKeyPtr pkeyNobjLibHelpDir(pkeyNobjLib.create(L"HELPDIR"));
+ lrc = pkeyNobjLibHelpDir.setValue(NULL, wszModule);
+ if (lrc != ERROR_SUCCESS)
+ return SELFREG_E_TYPELIB;
+ }
+#ifdef RT_EXCEPTIONS_ENABLED
+ catch (AdHocRegError) { return SELFREG_E_TYPELIB; }
+#endif
+
+ return S_OK;
+}
+
+
+STDAPI DllUnregisterServer(void)
+{
+ static struct { HKEY hKeyRoot; wchar_t const *pwszParentKey; wchar_t const *pwszKeyToDelete; HRESULT hrcFail; }
+ s_aKeys[] =
+ {
+ { HKEY_CLASSES_ROOT, L"TypeLib", L"{2A0C94D1-40E1-439C-8FE8-24107CAB0840}", SELFREG_E_TYPELIB },
+ { HKEY_CLASSES_ROOT, L"CLSID", L"{f374d1a0-bf08-4bdc-9cb2-c15ddaeef955}", SELFREG_E_CLASS },
+ };
+
+ HRESULT hrc = S_OK;
+ for (size_t i = 0; i < RT_ELEMENTS(s_aKeys); i++)
+ {
+ HKEY hKey = NULL;
+ LSTATUS lrc = RegOpenKeyExW(s_aKeys[i].hKeyRoot, s_aKeys[i].pwszParentKey, 0, KEY_ALL_ACCESS, &hKey);
+ if (lrc == ERROR_SUCCESS)
+ {
+ lrc = RegDeleteTreeW(hKey, s_aKeys[i].pwszKeyToDelete); /* Vista and later */
+ RegCloseKey(hKey);
+ }
+
+ if (lrc != ERROR_SUCCESS && lrc != ERROR_FILE_NOT_FOUND && hrc == S_OK)
+ hrc = s_aKeys[i].hrcFail;
+ }
+
+ return S_OK;
+}
+
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobj.def b/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobj.def
new file mode 100644
index 00000000..abfe3834
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobj.def
@@ -0,0 +1,42 @@
+; $Id: VBoxNetFltNobj.def $
+; @file
+; VBoxNetFltNobj.def - Notify Object for Bridged Networking Driver.
+; Library def file
+;
+
+;
+; Copyright (C) 2011-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+LIBRARY VBoxNetFltNobj
+EXPORTS
+ DllCanUnloadNow PRIVATE
+ DllGetClassObject PRIVATE
+ DllRegisterServer PRIVATE
+ DllUnregisterServer PRIVATE
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobj.h b/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobj.h
new file mode 100644
index 00000000..87eed992
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobj.h
@@ -0,0 +1,96 @@
+/* $Id: VBoxNetFltNobj.h $ */
+/** @file
+ * VBoxNetFltNobj.h - Notify Object for Bridged Networking Driver.
+ * Used to filter Bridged Networking Driver bindings
+ */
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_SRC_VBoxNetFlt_win_nobj_VBoxNetFltNobj_h
+#define VBOX_INCLUDED_SRC_VBoxNetFlt_win_nobj_VBoxNetFltNobj_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <iprt/win/windows.h>
+
+#include "VBox/com/defs.h"
+#include "VBoxNetFltNobjT.h"
+#include "VBoxNetFltNobjRc.h"
+
+#define VBOXNETFLTNOTIFY_ONFAIL_BINDDEFAULT false
+
+/*
+ * VirtualBox Bridging driver notify object.
+ * Needed to make our driver bind to "real" host adapters only
+ */
+class ATL_NO_VTABLE VBoxNetFltNobj
+ : public ATL::CComObjectRootEx<ATL::CComMultiThreadModel>
+ , public ATL::CComCoClass<VBoxNetFltNobj, &CLSID_VBoxNetFltNobj>
+ , public INetCfgComponentControl
+ , public INetCfgComponentNotifyBinding
+{
+public:
+ VBoxNetFltNobj();
+ virtual ~VBoxNetFltNobj();
+
+ BEGIN_COM_MAP(VBoxNetFltNobj)
+ COM_INTERFACE_ENTRY(INetCfgComponentControl)
+ COM_INTERFACE_ENTRY(INetCfgComponentNotifyBinding)
+ END_COM_MAP()
+
+ // this is a "just in case" conditional, which is not defined
+#ifdef VBOX_FORCE_REGISTER_SERVER
+ DECLARE_REGISTRY_RESOURCEID(IDR_VBOXNETFLT_NOBJ)
+#endif
+
+ /* INetCfgComponentControl methods */
+ STDMETHOD(Initialize)(IN INetCfgComponent *pNetCfgComponent, IN INetCfg *pNetCfg, IN BOOL bInstalling);
+ STDMETHOD(ApplyRegistryChanges)();
+ STDMETHOD(ApplyPnpChanges)(IN INetCfgPnpReconfigCallback *pCallback);
+ STDMETHOD(CancelChanges)();
+
+ /* INetCfgComponentNotifyBinding methods */
+ STDMETHOD(NotifyBindingPath)(IN DWORD dwChangeFlag, IN INetCfgBindingPath *pNetCfgBP);
+ STDMETHOD(QueryBindingPath)(IN DWORD dwChangeFlag, IN INetCfgBindingPath *pNetCfgBP);
+private:
+
+ void init(IN INetCfgComponent *pNetCfgComponent, IN INetCfg *pNetCfg, IN BOOL bInstalling);
+ void cleanup();
+
+ /* these two used to maintain the component info passed to
+ * INetCfgComponentControl::Initialize */
+ INetCfg *mpNetCfg;
+ INetCfgComponent *mpNetCfgComponent;
+ BOOL mbInstalling;
+};
+
+#endif /* !VBOX_INCLUDED_SRC_VBoxNetFlt_win_nobj_VBoxNetFltNobj_h */
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobj.rc b/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobj.rc
new file mode 100644
index 00000000..4a1398c6
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobj.rc
@@ -0,0 +1,78 @@
+/* $Id: VBoxNetFltNobj.rc $ */
+/** @file
+ * VBoxNetFltNobj - Resource file containing version info and icon.
+ */
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#include <windows.h>
+#include <VBox/version.h>
+
+#include "VBoxNetFltNobjRc.h"
+
+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_DLL
+ FILESUBTYPE VFT2_UNKNOWN
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904E4" // Lang=US English, CharSet=Windows Multilingual
+ BEGIN
+ VALUE "FileDescription", "VirtualBox Bridged Networking Driver Notify Object v1.1\0"
+ VALUE "InternalName", "VBoxNetFltNobj\0"
+ VALUE "OriginalFilename", "VBoxNetFltNobj.dll\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, 1252
+ END
+END
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// REGISTRY
+//
+
+IDR_VBOXNETFLT_NOBJ REGISTRY "VBoxNetFltNobj.rgs"
+
+1 TYPELIB "VBoxNetFltNobjT.tlb"
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobj.rgs b/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobj.rgs
new file mode 100644
index 00000000..d6f9bae3
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobj.rgs
@@ -0,0 +1,13 @@
+HKCR
+{
+ NoRemove CLSID
+ {
+ ForceRemove {f374d1a0-bf08-4bdc-9cb2-c15ddaeef955} = s 'VirtualBox Bridged Networking Driver Notify Object v1.1'
+ {
+ InProcServer32 = s '%MODULE%'
+ {
+ val ThreadingModel = s 'Both'
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobjRc.h b/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobjRc.h
new file mode 100644
index 00000000..187af500
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobjRc.h
@@ -0,0 +1,46 @@
+/* $Id: VBoxNetFltNobjRc.h $ */
+/** @file
+ * VBoxNetFltNobjRc.h - Notify Object for Bridged Networking Driver.
+ * Resource definitions
+ */
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_SRC_VBoxNetFlt_win_nobj_VBoxNetFltNobjRc_h
+#define VBOX_INCLUDED_SRC_VBoxNetFlt_win_nobj_VBoxNetFltNobjRc_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* registry script rc ID */
+#define IDR_VBOXNETFLT_NOBJ 101
+
+#endif /* !VBOX_INCLUDED_SRC_VBoxNetFlt_win_nobj_VBoxNetFltNobjRc_h */
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobjT.idl b/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobjT.idl
new file mode 100644
index 00000000..9bf8b6f9
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobjT.idl
@@ -0,0 +1,55 @@
+/* $Id: VBoxNetFltNobjT.idl $ */
+/** @file
+ * VBoxNetFltNobjT.idl - Notify Object for Bridged Networking Driver, typelib definition.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#include <netcfgn.idl>
+
+[
+ uuid(2a0c94d1-40e1-439c-8fe8-24107cab0840),
+ version(1.1),
+ helpstring("VirtualBox Bridged Networking Driver Notify Object v1.1 Type Library")
+]
+library VBoxNetFltNobjLib
+{
+ [
+ uuid(f374d1a0-bf08-4bdc-9cb2-c15ddaeef955),
+ helpstring("VirtualBox Bridged Networking Driver Notify Object Class")
+ ]
+ coclass VBoxNetFltNobj
+ {
+ [restricted] interface INetCfgComponentControl;
+ [restricted] interface INetCfgComponentNotifyBinding;
+ };
+};
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/tools/Makefile.kup b/src/VBox/HostDrivers/VBoxNetFlt/win/tools/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/win/tools/Makefile.kup
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/tools/VBoxNetAdpInstall.cpp b/src/VBox/HostDrivers/VBoxNetFlt/win/tools/VBoxNetAdpInstall.cpp
new file mode 100644
index 00000000..40a79b1a
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/win/tools/VBoxNetAdpInstall.cpp
@@ -0,0 +1,341 @@
+/* $Id: VBoxNetAdpInstall.cpp $ */
+/** @file
+ * NetAdpInstall - VBoxNetAdp installer command line tool.
+ */
+
+/*
+ * 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 <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <VBox/VBoxNetCfg-win.h>
+#include <VBox/VBoxDrvCfg-win.h>
+#include <devguid.h>
+
+#include <iprt/initterm.h>
+#include <iprt/message.h>
+#include <iprt/process.h>
+#include <iprt/stream.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#define VBOX_NETADP_APP_NAME L"NetAdpInstall"
+
+#define VBOX_NETADP_HWID L"sun_VBoxNetAdp"
+#ifdef NDIS60
+# define VBOX_NETADP_INF L"VBoxNetAdp6.inf"
+#else
+# define VBOX_NETADP_INF L"VBoxNetAdp.inf"
+#endif
+
+
+static DECLCALLBACK(void) winNetCfgLogger(const char *pszString)
+{
+ RTMsgInfo("%s", pszString);
+}
+
+
+/** Wrapper around GetfullPathNameW that will try an alternative INF location.
+ *
+ * The default location is the current directory. If not found there, the
+ * alternative location is the executable directory. If not found there either,
+ * the first alternative is present to the caller.
+ */
+static DWORD MyGetfullPathNameW(LPCWSTR pwszName, size_t cchFull, LPWSTR pwszFull)
+{
+ LPWSTR pwszFilePart;
+ DWORD dwSize = GetFullPathNameW(pwszName, (DWORD)cchFull, pwszFull, &pwszFilePart);
+ if (dwSize <= 0)
+ return dwSize;
+
+ /* if it doesn't exist, see if the file exists in the same directory as the executable. */
+ if (GetFileAttributesW(pwszFull) == INVALID_FILE_ATTRIBUTES)
+ {
+ WCHAR wsz[512];
+ DWORD cch = GetModuleFileNameW(GetModuleHandle(NULL), &wsz[0], RT_ELEMENTS(wsz));
+ if (cch > 0)
+ {
+ while (cch > 0 && wsz[cch - 1] != '/' && wsz[cch - 1] != '\\' && wsz[cch - 1] != ':')
+ cch--;
+ unsigned i = 0;
+ while (cch < sizeof(wsz) / sizeof(wsz[0]))
+ {
+ wsz[cch] = pwszFilePart[i++];
+ if (!wsz[cch])
+ {
+ dwSize = GetFullPathNameW(wsz, (DWORD)cchFull, pwszFull, NULL);
+ if (dwSize > 0 && GetFileAttributesW(pwszFull) != INVALID_FILE_ATTRIBUTES)
+ return dwSize;
+ break;
+ }
+ cch++;
+ }
+ }
+ }
+
+ /* fallback */
+ return GetFullPathNameW(pwszName, (DWORD)cchFull, pwszFull, NULL);
+}
+
+
+static int VBoxNetAdpInstall(void)
+{
+ RTMsgInfo("Adding host-only interface...");
+ VBoxNetCfgWinSetLogging(winNetCfgLogger);
+
+ HRESULT hr = CoInitialize(NULL);
+ if (SUCCEEDED(hr))
+ {
+ WCHAR wszInfFile[MAX_PATH];
+ DWORD cwcInfFile = MyGetfullPathNameW(VBOX_NETADP_INF, RT_ELEMENTS(wszInfFile), wszInfFile);
+ if (cwcInfFile > 0)
+ {
+ INetCfg *pnc;
+ LPWSTR lpszLockedBy = NULL;
+ hr = VBoxNetCfgWinQueryINetCfg(&pnc, TRUE, VBOX_NETADP_APP_NAME, 10000, &lpszLockedBy);
+ if (hr == S_OK)
+ {
+
+ hr = VBoxNetCfgWinNetAdpInstall(pnc, wszInfFile);
+
+ if (hr == S_OK)
+ RTMsgInfo("Installed successfully!");
+ else
+ RTMsgError("failed to install VBoxNetAdp: %Rhrc", hr);
+
+ VBoxNetCfgWinReleaseINetCfg(pnc, TRUE);
+ }
+ else
+ RTMsgError("VBoxNetCfgWinQueryINetCfg failed: %Rhrc", hr);
+ /*
+ hr = VBoxDrvCfgInfInstall(MpInf);
+ if (FAILED(hr))
+ printf("VBoxDrvCfgInfInstall failed %#x\n", hr);
+
+ GUID guid;
+ BSTR name, errMsg;
+
+ hr = VBoxNetCfgWinCreateHostOnlyNetworkInterface (MpInf, true, &guid, &name, &errMsg);
+ if (SUCCEEDED(hr))
+ {
+ ULONG ip, mask;
+ hr = VBoxNetCfgWinGenHostOnlyNetworkNetworkIp(&ip, &mask);
+ if (SUCCEEDED(hr))
+ {
+ // ip returned by VBoxNetCfgWinGenHostOnlyNetworkNetworkIp is a network ip,
+ // i.e. 192.168.xxx.0, assign 192.168.xxx.1 for the hostonly adapter
+ ip = ip | (1 << 24);
+ hr = VBoxNetCfgWinEnableStaticIpConfig(&guid, ip, mask);
+ if (SUCCEEDED(hr))
+ printf("installation successful\n");
+ else
+ printf("VBoxNetCfgWinEnableStaticIpConfig failed: hr=%#lx\n", hr);
+ }
+ else
+ printf("VBoxNetCfgWinGenHostOnlyNetworkNetworkIp failed: hr=%#lx\n", hr);
+ }
+ else
+ printf("VBoxNetCfgWinCreateHostOnlyNetworkInterface failed: hr=%#lx\n", hr);
+ */
+ }
+ else
+ {
+ DWORD dwErr = GetLastError();
+ RTMsgError("MyGetfullPathNameW failed: %Rwc", dwErr);
+ hr = HRESULT_FROM_WIN32(dwErr);
+ }
+ CoUninitialize();
+ }
+ else
+ RTMsgError("Failed initializing COM: %Rhrc", hr);
+
+ VBoxNetCfgWinSetLogging(NULL);
+
+ return SUCCEEDED(hr) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+static int VBoxNetAdpUninstall(void)
+{
+ RTMsgInfo("Uninstalling all host-only interfaces...");
+ VBoxNetCfgWinSetLogging(winNetCfgLogger);
+
+ HRESULT hr = CoInitialize(NULL);
+ if (SUCCEEDED(hr))
+ {
+ hr = VBoxNetCfgWinRemoveAllNetDevicesOfId(VBOX_NETADP_HWID);
+ if (SUCCEEDED(hr))
+ {
+ hr = VBoxDrvCfgInfUninstallAllSetupDi(&GUID_DEVCLASS_NET, L"Net", VBOX_NETADP_HWID, 0/* could be SUOI_FORCEDELETE */);
+ if (SUCCEEDED(hr))
+ RTMsgInfo("Uninstallation successful!");
+ else
+ RTMsgWarning("uninstalled successfully, but failed to remove infs (%Rhrc)\n", hr);
+ }
+ else
+ RTMsgError("uninstall failed: %Rhrc", hr);
+ CoUninitialize();
+ }
+ else
+ RTMsgError("Failed initializing COM: %Rhrc", hr);
+
+ VBoxNetCfgWinSetLogging(NULL);
+
+ return SUCCEEDED(hr) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+static int VBoxNetAdpUpdate(void)
+{
+ RTMsgInfo("Uninstalling all host-only interfaces...");
+ VBoxNetCfgWinSetLogging(winNetCfgLogger);
+
+ HRESULT hr = CoInitialize(NULL);
+ if (SUCCEEDED(hr))
+ {
+ BOOL fRebootRequired = FALSE;
+ /*
+ * Before we can update the driver for existing adapters we need to remove
+ * all old driver packages from the driver cache. Otherwise we may end up
+ * with both NDIS5 and NDIS6 versions of VBoxNetAdp in the cache which
+ * will cause all sorts of trouble.
+ */
+ VBoxDrvCfgInfUninstallAllF(L"Net", VBOX_NETADP_HWID, SUOI_FORCEDELETE);
+ hr = VBoxNetCfgWinUpdateHostOnlyNetworkInterface(VBOX_NETADP_INF, &fRebootRequired, VBOX_NETADP_HWID);
+ if (SUCCEEDED(hr))
+ {
+ if (fRebootRequired)
+ RTMsgWarning("!!REBOOT REQUIRED!!");
+ RTMsgInfo("Updated successfully!");
+ }
+ else
+ RTMsgError("update failed: %Rhrc", hr);
+
+ CoUninitialize();
+ }
+ else
+ RTMsgError("Failed initializing COM: %Rhrc", hr);
+
+ VBoxNetCfgWinSetLogging(NULL);
+
+ return SUCCEEDED(hr) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+static int VBoxNetAdpDisable(void)
+{
+ RTMsgInfo("Disabling all host-only interfaces...");
+ VBoxNetCfgWinSetLogging(winNetCfgLogger);
+
+ HRESULT hr = CoInitialize(NULL);
+ if (SUCCEEDED(hr))
+ {
+ hr = VBoxNetCfgWinPropChangeAllNetDevicesOfId(VBOX_NETADP_HWID, VBOXNECTFGWINPROPCHANGE_TYPE_DISABLE);
+ if (SUCCEEDED(hr))
+ RTMsgInfo("Disabling successful");
+ else
+ RTMsgError("disable failed: %Rhrc", hr);
+
+ CoUninitialize();
+ }
+ else
+ RTMsgError("Failed initializing COM: %Rhrc", hr);
+
+ VBoxNetCfgWinSetLogging(NULL);
+
+ return SUCCEEDED(hr) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+static int VBoxNetAdpEnable(void)
+{
+ RTMsgInfo("Enabling all host-only interfaces...");
+ VBoxNetCfgWinSetLogging(winNetCfgLogger);
+
+ HRESULT hr = CoInitialize(NULL);
+ if (SUCCEEDED(hr))
+ {
+ hr = VBoxNetCfgWinPropChangeAllNetDevicesOfId(VBOX_NETADP_HWID, VBOXNECTFGWINPROPCHANGE_TYPE_ENABLE);
+ if (SUCCEEDED(hr))
+ RTMsgInfo("Enabling successful!");
+ else
+ RTMsgError("enabling failed: %hrc", hr);
+
+ CoUninitialize();
+ }
+ else
+ RTMsgError("Failed initializing COM: %Rhrc", hr);
+
+ VBoxNetCfgWinSetLogging(NULL);
+
+ return SUCCEEDED(hr) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+static void printUsage(void)
+{
+ RTPrintf("host-only network adapter configuration tool\n"
+ " Usage: %s [cmd]\n"
+ " cmd can be one of the following values:\n"
+ " i - install a new host-only interface (default command)\n"
+ " u - uninstall all host-only interfaces\n"
+ " a - update the host-only driver\n"
+ " d - disable all host-only interfaces\n"
+ " e - enable all host-only interfaces\n"
+ " h - print this message\n",
+ RTProcShortName());
+}
+
+int __cdecl main(int argc, char **argv)
+{
+ RTR3InitExe(argc, &argv, 0);
+
+ if (argc < 2)
+ return VBoxNetAdpInstall();
+ if (argc > 2)
+ {
+ printUsage();
+ return RTEXITCODE_SYNTAX;
+ }
+
+ if (!strcmp(argv[1], "i"))
+ return VBoxNetAdpInstall();
+ if (!strcmp(argv[1], "u"))
+ return VBoxNetAdpUninstall();
+ if (!strcmp(argv[1], "a"))
+ return VBoxNetAdpUpdate();
+ if (!strcmp(argv[1], "d"))
+ return VBoxNetAdpDisable();
+ if (!strcmp(argv[1], "e"))
+ return VBoxNetAdpEnable();
+
+ printUsage();
+ return !strcmp(argv[1], "h") ? RTEXITCODE_SUCCESS : RTEXITCODE_SYNTAX;
+}
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/tools/VBoxNetAdpUninstall.cpp b/src/VBox/HostDrivers/VBoxNetFlt/win/tools/VBoxNetAdpUninstall.cpp
new file mode 100644
index 00000000..03508669
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/win/tools/VBoxNetAdpUninstall.cpp
@@ -0,0 +1,107 @@
+/* $Id: VBoxNetAdpUninstall.cpp $ */
+/** @file
+ * NetAdpUninstall - VBoxNetAdp uninstaller command line tool
+ */
+
+/*
+ * 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 <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <VBox/VBoxNetCfg-win.h>
+#include <VBox/VBoxDrvCfg-win.h>
+
+#include <devguid.h>
+
+#include <iprt/initterm.h>
+#include <iprt/message.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#ifdef NDIS60
+# define VBOX_NETADP_HWID L"sun_VBoxNetAdp6"
+#else
+# define VBOX_NETADP_HWID L"sun_VBoxNetAdp"
+#endif
+
+
+static DECLCALLBACK(void) winNetCfgLogger(const char *pszString)
+{
+ RTMsgInfo("%s", pszString);
+}
+
+static int VBoxNetAdpUninstall(void)
+{
+ RTMsgInfo("Uninstalling all Host-Only interfaces ...");
+
+ int rcExit = RTEXITCODE_FAILURE;
+ VBoxNetCfgWinSetLogging(winNetCfgLogger);
+
+ HRESULT hr = CoInitialize(NULL);
+ if (hr == S_OK)
+ {
+ hr = VBoxNetCfgWinRemoveAllNetDevicesOfId(VBOX_NETADP_HWID);
+ if (hr == S_OK)
+ {
+ hr = VBoxDrvCfgInfUninstallAllSetupDi(&GUID_DEVCLASS_NET, L"Net", VBOX_NETADP_HWID, 0/* could be SUOI_FORCEDELETE */);
+ if (hr == S_OK)
+ RTMsgInfo("Uninstalled successfully!");
+ else
+ RTMsgError("uninstalled successfully, but failed to remove infs (%Rhrc)\n", hr);
+ rcExit = RTEXITCODE_SUCCESS;
+ }
+ else
+ RTMsgError("uninstall failed: %Rhrc", hr);
+
+ CoUninitialize();
+ }
+ else
+ RTMsgError("Failed initializing COM: %Rhrc", hr);
+
+ VBoxNetCfgWinSetLogging(NULL);
+
+ return rcExit;
+}
+
+int __cdecl main(int argc, char **argv)
+{
+ RTR3InitExeNoArguments(0);
+ if (argc != 1)
+ return RTMsgErrorExit(RTEXITCODE_SYNTAX, "This utility takes no arguments\n");
+ NOREF(argv);
+
+ return VBoxNetAdpUninstall();
+}
+
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/tools/VBoxNetFltInstall.cpp b/src/VBox/HostDrivers/VBoxNetFlt/win/tools/VBoxNetFltInstall.cpp
new file mode 100644
index 00000000..e6c4f2de
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/win/tools/VBoxNetFltInstall.cpp
@@ -0,0 +1,201 @@
+/* $Id: VBoxNetFltInstall.cpp $ */
+/** @file
+ * NetFltInstall - VBoxNetFlt installer command line tool
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <VBox/VBoxNetCfg-win.h>
+#include <devguid.h>
+#include <stdio.h>
+
+#include <iprt/initterm.h>
+#include <iprt/message.h>
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define NETFLT_ID L"sun_VBoxNetFlt"
+#define VBOX_NETCFG_APP_NAME L"NetFltInstall"
+#define VBOX_NETFLT_PT_INF L".\\VBoxNetFlt.inf"
+#define VBOX_NETFLT_MP_INF L".\\VBoxNetFltM.inf"
+#define VBOX_NETFLT_RETRIES 10
+
+
+static DECLCALLBACK(void) winNetCfgLogger(const char *pszString)
+{
+ printf("%s", pszString);
+}
+
+/** Wrapper around GetfullPathNameW that will try an alternative INF location.
+ *
+ * The default location is the current directory. If not found there, the
+ * alternative location is the executable directory. If not found there either,
+ * the first alternative is present to the caller.
+ */
+static DWORD MyGetfullPathNameW(LPCWSTR pwszName, size_t cchFull, LPWSTR pwszFull)
+{
+ LPWSTR pwszFilePart;
+ DWORD dwSize = GetFullPathNameW(pwszName, (DWORD)cchFull, pwszFull, &pwszFilePart);
+ if (dwSize <= 0)
+ return dwSize;
+
+ /* if it doesn't exist, see if the file exists in the same directory as the executable. */
+ if (GetFileAttributesW(pwszFull) == INVALID_FILE_ATTRIBUTES)
+ {
+ WCHAR wsz[512];
+ DWORD cch = GetModuleFileNameW(GetModuleHandle(NULL), &wsz[0], RT_ELEMENTS(wsz));
+ if (cch > 0)
+ {
+ while (cch > 0 && wsz[cch - 1] != '/' && wsz[cch - 1] != '\\' && wsz[cch - 1] != ':')
+ cch--;
+ unsigned i = 0;
+ while (cch < RT_ELEMENTS(wsz))
+ {
+ wsz[cch] = pwszFilePart[i++];
+ if (!wsz[cch])
+ {
+ dwSize = GetFullPathNameW(wsz, (DWORD)cchFull, pwszFull, NULL);
+ if (dwSize > 0 && GetFileAttributesW(pwszFull) != INVALID_FILE_ATTRIBUTES)
+ return dwSize;
+ break;
+ }
+ cch++;
+ }
+ }
+ }
+
+ /* fallback */
+ return GetFullPathNameW(pwszName, (DWORD)cchFull, pwszFull, NULL);
+}
+
+static int VBoxNetFltInstall()
+{
+ WCHAR wszPtInf[MAX_PATH];
+ WCHAR wszMpInf[MAX_PATH];
+ INetCfg *pnc;
+ int rcExit = RTEXITCODE_FAILURE;
+
+ VBoxNetCfgWinSetLogging(winNetCfgLogger);
+
+ HRESULT hr = CoInitialize(NULL);
+ if (hr == S_OK)
+ {
+ for (int i = 0;; i++)
+ {
+ LPWSTR pwszLockedBy = NULL;
+ hr = VBoxNetCfgWinQueryINetCfg(&pnc, TRUE, VBOX_NETCFG_APP_NAME, 10000, &pwszLockedBy);
+ if (hr == S_OK)
+ {
+ DWORD dwSize;
+ dwSize = MyGetfullPathNameW(VBOX_NETFLT_PT_INF, RT_ELEMENTS(wszPtInf), wszPtInf);
+ if (dwSize > 0)
+ {
+ /** @todo add size check for (RT_ELEMENTS(wszPtInf) == dwSize (string length in WCHARs) */
+
+ dwSize = MyGetfullPathNameW(VBOX_NETFLT_MP_INF, RT_ELEMENTS(wszMpInf), wszMpInf);
+ if (dwSize > 0)
+ {
+ /** @todo add size check for (RT_ELEMENTS(wszMpInf) == dwSize (string length in WHCARs) */
+
+ LPCWSTR apwszInfs[] = { wszPtInf, wszMpInf };
+ hr = VBoxNetCfgWinNetFltInstall(pnc, apwszInfs, 2);
+ if (hr == S_OK)
+ {
+ wprintf(L"installed successfully\n");
+ rcExit = RTEXITCODE_SUCCESS;
+ }
+ else
+ wprintf(L"error installing VBoxNetFlt (%#lx)\n", hr);
+ }
+ else
+ {
+ hr = HRESULT_FROM_WIN32(GetLastError());
+ wprintf(L"error getting full inf path for VBoxNetFltM.inf (%#lx)\n", hr);
+ }
+ }
+ else
+ {
+ hr = HRESULT_FROM_WIN32(GetLastError());
+ wprintf(L"error getting full inf path for VBoxNetFlt.inf (%#lx)\n", hr);
+ }
+
+ VBoxNetCfgWinReleaseINetCfg(pnc, TRUE);
+ break;
+ }
+
+ if (hr == NETCFG_E_NO_WRITE_LOCK && pwszLockedBy)
+ {
+ if (i < VBOX_NETFLT_RETRIES && !wcscmp(pwszLockedBy, L"6to4svc.dll"))
+ {
+ wprintf(L"6to4svc.dll is holding the lock, retrying %d out of %d\n", i + 1, VBOX_NETFLT_RETRIES);
+ CoTaskMemFree(pwszLockedBy);
+ }
+ else
+ {
+ wprintf(L"Error: write lock is owned by another application (%s), close the application and retry installing\n", pwszLockedBy);
+ CoTaskMemFree(pwszLockedBy);
+ break;
+ }
+ }
+ else
+ {
+ wprintf(L"Error getting the INetCfg interface (%#lx)\n", hr);
+ break;
+ }
+ }
+
+ CoUninitialize();
+ }
+ else
+ wprintf(L"Error initializing COM (%#lx)\n", hr);
+
+ VBoxNetCfgWinSetLogging(NULL);
+
+ return rcExit;
+}
+
+int __cdecl main(int argc, char **argv)
+{
+ RTR3InitExeNoArguments(0);
+ if (argc != 1)
+ return RTMsgErrorExit(RTEXITCODE_SYNTAX, "This utility takes no arguments\n");
+ NOREF(argv);
+
+ return VBoxNetFltInstall();
+}
+
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/tools/VBoxNetFltUninstall.cpp b/src/VBox/HostDrivers/VBoxNetFlt/win/tools/VBoxNetFltUninstall.cpp
new file mode 100644
index 00000000..c62d7726
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/win/tools/VBoxNetFltUninstall.cpp
@@ -0,0 +1,133 @@
+/* $Id: VBoxNetFltUninstall.cpp $ */
+/** @file
+ * NetFltUninstall - VBoxNetFlt uninstaller command line tool
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <VBox/VBoxNetCfg-win.h>
+#include <stdio.h>
+
+#include <iprt/initterm.h>
+#include <iprt/message.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#define NETFLT_ID L"sun_VBoxNetFlt"
+#define VBOX_NETCFG_APP_NAME L"NetFltUninstall"
+#define VBOX_NETFLT_PT_INF L".\\VBoxNetFlt.inf"
+#define VBOX_NETFLT_MP_INF L".\\VBoxNetFltM.inf"
+#define VBOX_NETFLT_RETRIES 10
+
+
+static DECLCALLBACK(void) winNetCfgLogger(const char *pszString)
+{
+ printf("%s", pszString);
+}
+
+static int VBoxNetFltUninstall()
+{
+ INetCfg *pnc;
+ int rcExit = RTEXITCODE_FAILURE;
+
+ VBoxNetCfgWinSetLogging(winNetCfgLogger);
+
+ HRESULT hr = CoInitialize(NULL);
+ if (hr == S_OK)
+ {
+ for (int i = 0;; i++)
+ {
+ LPWSTR pwszLockedBy = NULL;
+ hr = VBoxNetCfgWinQueryINetCfg(&pnc, TRUE, VBOX_NETCFG_APP_NAME, 10000, &pwszLockedBy);
+ if (hr == S_OK)
+ {
+ hr = VBoxNetCfgWinNetFltUninstall(pnc);
+ if (hr != S_OK && hr != S_FALSE)
+ wprintf(L"error uninstalling VBoxNetFlt (%#lx)\n", hr);
+ else
+ {
+ wprintf(L"uninstalled successfully\n");
+ rcExit = RTEXITCODE_SUCCESS;
+ }
+
+ VBoxNetCfgWinReleaseINetCfg(pnc, TRUE);
+ break;
+ }
+
+ if (hr == NETCFG_E_NO_WRITE_LOCK && pwszLockedBy)
+ {
+ if (i < VBOX_NETFLT_RETRIES && !wcscmp(pwszLockedBy, L"6to4svc.dll"))
+ {
+ wprintf(L"6to4svc.dll is holding the lock, retrying %d out of %d\n", i + 1, VBOX_NETFLT_RETRIES);
+ CoTaskMemFree(pwszLockedBy);
+ }
+ else
+ {
+ wprintf(L"Error: write lock is owned by another application (%s), close the application and retry uninstalling\n",
+ pwszLockedBy);
+ CoTaskMemFree(pwszLockedBy);
+ break;
+ }
+ }
+ else
+ {
+ wprintf(L"Error getting the INetCfg interface (%#lx)\n", hr);
+ break;
+ }
+ }
+
+ CoUninitialize();
+ }
+ else
+ wprintf(L"Error initializing COM (%#lx)\n", hr);
+
+ VBoxNetCfgWinSetLogging(NULL);
+
+ return rcExit;
+}
+
+int __cdecl main(int argc, char **argv)
+{
+ RTR3InitExeNoArguments(0);
+ if (argc != 1)
+ return RTMsgErrorExit(RTEXITCODE_SYNTAX, "This utility takes no arguments\n");
+ NOREF(argv);
+
+ return VBoxNetFltUninstall();
+}
+
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/tools/VBoxNetLwfInstall.cpp b/src/VBox/HostDrivers/VBoxNetFlt/win/tools/VBoxNetLwfInstall.cpp
new file mode 100644
index 00000000..1b8cf693
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/win/tools/VBoxNetLwfInstall.cpp
@@ -0,0 +1,186 @@
+/* $Id: VBoxNetLwfInstall.cpp $ */
+/** @file
+ * NetLwfInstall - VBoxNetLwf installer command line tool
+ */
+
+/*
+ * 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 <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <VBox/VBoxNetCfg-win.h>
+#include <devguid.h>
+
+#include <iprt/initterm.h>
+#include <iprt/message.h>
+#include <iprt/utf16.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#define VBOX_NETCFG_APP_NAME L"NetLwfInstall"
+#define VBOX_NETLWF_INF L".\\VBoxNetLwf.inf"
+#define VBOX_NETLWF_RETRIES 10
+
+
+static DECLCALLBACK(void) winNetCfgLogger(const char *pszString)
+{
+ RTMsgInfo("%s", pszString);
+}
+
+/** Wrapper around GetfullPathNameW that will try an alternative INF location.
+ *
+ * The default location is the current directory. If not found there, the
+ * alternative location is the executable directory. If not found there either,
+ * the first alternative is present to the caller.
+ */
+static DWORD MyGetfullPathNameW(LPCWSTR pwszName, size_t cchFull, LPWSTR pwszFull)
+{
+ LPWSTR pwszFilePart;
+ DWORD dwSize = GetFullPathNameW(pwszName, (DWORD)cchFull, pwszFull, &pwszFilePart);
+ if (dwSize <= 0)
+ return dwSize;
+
+ /* if it doesn't exist, see if the file exists in the same directory as the executable. */
+ if (GetFileAttributesW(pwszFull) == INVALID_FILE_ATTRIBUTES)
+ {
+ WCHAR wsz[512];
+ DWORD cch = GetModuleFileNameW(GetModuleHandle(NULL), &wsz[0], RT_ELEMENTS(wsz));
+ if (cch > 0)
+ {
+ while (cch > 0 && wsz[cch - 1] != '/' && wsz[cch - 1] != '\\' && wsz[cch - 1] != ':')
+ cch--;
+ unsigned i = 0;
+ while (cch < RT_ELEMENTS(wsz))
+ {
+ wsz[cch] = pwszFilePart[i++];
+ if (!wsz[cch])
+ {
+ dwSize = GetFullPathNameW(wsz, (DWORD)cchFull, pwszFull, NULL);
+ if (dwSize > 0 && GetFileAttributesW(pwszFull) != INVALID_FILE_ATTRIBUTES)
+ return dwSize;
+ break;
+ }
+ cch++;
+ }
+ }
+ }
+
+ /* fallback */
+ return GetFullPathNameW(pwszName, (DWORD)cchFull, pwszFull, NULL);
+}
+
+static int VBoxNetLwfInstall()
+{
+ WCHAR wszInf[MAX_PATH];
+ INetCfg *pnc;
+ int rcExit = RTEXITCODE_FAILURE;
+
+ VBoxNetCfgWinSetLogging(winNetCfgLogger);
+
+ HRESULT hr = CoInitialize(NULL);
+ if (hr == S_OK)
+ {
+ for (int i = 0;; i++)
+ {
+ LPWSTR pwszLockedBy = NULL;
+ hr = VBoxNetCfgWinQueryINetCfg(&pnc, TRUE, VBOX_NETCFG_APP_NAME, 10000, &pwszLockedBy);
+ if (hr == S_OK)
+ {
+ DWORD dwSize;
+ dwSize = MyGetfullPathNameW(VBOX_NETLWF_INF, RT_ELEMENTS(wszInf), wszInf);
+ if (dwSize > 0)
+ {
+ /** @todo add size check for (RT_ELEMENTS(wszInf) == dwSize (string length in WCHARs) */
+ hr = VBoxNetCfgWinNetLwfInstall(pnc, wszInf);
+ if (hr == S_OK)
+ {
+ RTMsgInfo("Installed successfully!");
+ rcExit = RTEXITCODE_SUCCESS;
+ }
+ else
+ RTMsgError("Failed installing VBoxNetLwf: %Rhrc", hr);
+ }
+ else
+ {
+ hr = HRESULT_FROM_WIN32(GetLastError());
+ RTMsgError("Failed getting full inf path for VBoxNetLwf.inf: %Rhrc", hr);
+ }
+
+ VBoxNetCfgWinReleaseINetCfg(pnc, TRUE);
+ break;
+ }
+
+ if (hr == NETCFG_E_NO_WRITE_LOCK && pwszLockedBy)
+ {
+ if (i < VBOX_NETLWF_RETRIES && RTUtf16ICmpAscii(pwszLockedBy, "6to4svc.dll") == 0)
+ {
+ RTMsgInfo("6to4svc.dll is holding the lock - retrying %d out of %d\n", i + 1, VBOX_NETLWF_RETRIES);
+ CoTaskMemFree(pwszLockedBy);
+ }
+ else
+ {
+ RTMsgError("write lock is owned by another application (%ls), close the application and retry installing",
+ pwszLockedBy);
+ CoTaskMemFree(pwszLockedBy);
+ break;
+ }
+ }
+ else
+ {
+ RTMsgError("Failed getting the INetCfg interface: %Rhrc", hr);
+ break;
+ }
+ }
+
+ CoUninitialize();
+ }
+ else
+ RTMsgError("Failed initializing COM: %Rhrc", hr);
+
+ VBoxNetCfgWinSetLogging(NULL);
+
+ return rcExit;
+}
+
+int __cdecl main(int argc, char **argv)
+{
+ RTR3InitExeNoArguments(0);
+ if (argc != 1)
+ return RTMsgErrorExit(RTEXITCODE_SYNTAX, "This utility takes no arguments\n");
+ NOREF(argv);
+
+ return VBoxNetLwfInstall();
+}
+
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/tools/VBoxNetLwfUninstall.cpp b/src/VBox/HostDrivers/VBoxNetFlt/win/tools/VBoxNetLwfUninstall.cpp
new file mode 100644
index 00000000..0648a127
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/win/tools/VBoxNetLwfUninstall.cpp
@@ -0,0 +1,130 @@
+/* $Id: VBoxNetLwfUninstall.cpp $ */
+/** @file
+ * NetLwfUninstall - VBoxNetLwf uninstaller command line tool
+ */
+
+/*
+ * 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 <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <VBox/VBoxNetCfg-win.h>
+
+#include <iprt/initterm.h>
+#include <iprt/message.h>
+#include <iprt/utf16.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#define VBOX_NETCFG_APP_NAME L"NetLwfUninstall"
+#define VBOX_NETLWF_RETRIES 10
+
+
+static DECLCALLBACK(void) winNetCfgLogger(const char *pszString)
+{
+ RTMsgInfo("%s", pszString);
+}
+
+static int VBoxNetLwfUninstall()
+{
+ int rcExit = RTEXITCODE_FAILURE;
+
+ VBoxNetCfgWinSetLogging(winNetCfgLogger);
+
+ HRESULT hr = CoInitialize(NULL);
+ if (hr == S_OK)
+ {
+ for (int i = 0;; i++)
+ {
+ LPWSTR pwszLockedBy = NULL;
+ INetCfg *pnc = NULL;
+ hr = VBoxNetCfgWinQueryINetCfg(&pnc, TRUE, VBOX_NETCFG_APP_NAME, 10000, &pwszLockedBy);
+ if (hr == S_OK)
+ {
+ hr = VBoxNetCfgWinNetLwfUninstall(pnc);
+ if (hr == S_OK)
+ {
+ RTMsgInfo("uninstalled successfully!");
+ rcExit = RTEXITCODE_SUCCESS;
+ }
+ else
+ RTMsgError("error uninstalling VBoxNetLwf: %Rhrc");
+
+ VBoxNetCfgWinReleaseINetCfg(pnc, TRUE);
+ break;
+ }
+
+ if (hr == NETCFG_E_NO_WRITE_LOCK && pwszLockedBy)
+ {
+ if (i < VBOX_NETLWF_RETRIES && RTUtf16ICmpAscii(pwszLockedBy, "6to4svc.dll") == 0)
+ {
+ RTMsgInfo("6to4svc.dll is holding the lock - retry %d out of %d ...", i + 1, VBOX_NETLWF_RETRIES);
+ CoTaskMemFree(pwszLockedBy);
+ }
+ else
+ {
+ RTMsgError("Write lock is owned by another application (%ls), close the application and retry uninstalling",
+ pwszLockedBy);
+ CoTaskMemFree(pwszLockedBy);
+ break;
+ }
+ }
+ else
+ {
+ RTMsgError("Failed getting the INetCfg interface: %Rhrc", hr);
+ break;
+ }
+ }
+
+ CoUninitialize();
+ }
+ else
+ RTMsgError("Failed initializing COM: %Rhrc", hr);
+
+ VBoxNetCfgWinSetLogging(NULL);
+
+ return rcExit;
+}
+
+int __cdecl main(int argc, char **argv)
+{
+ RTR3InitExeNoArguments(0);
+ if (argc != 1)
+ return RTMsgErrorExit(RTEXITCODE_SYNTAX, "This utility takes no arguments\n");
+ NOREF(argv);
+
+ return VBoxNetLwfUninstall();
+}
+