diff options
Diffstat (limited to 'src/VBox/HostDrivers/VBoxNetFlt/win/drv')
15 files changed, 11721 insertions, 0 deletions
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, ¶m.ParameterData.StringData); + Assert(RT_SUCCESS(rc)); + if (RT_SUCCESS(rc)) + { + NdisWriteConfiguration(&Status, + hConfiguration, + &strMAC, + ¶m); + 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" |