diff options
Diffstat (limited to 'src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetLwf-win.cpp')
-rw-r--r-- | src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetLwf-win.cpp | 2736 |
1 files changed, 2736 insertions, 0 deletions
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; +} + |