VBoxNetLwf-win.cpp revision da34f66f1d7f32c00dcccd9164bc10435de15952
/* $Id$ */
/** @file
* VBoxNetLwf-win.cpp - NDIS6 Bridged Networking Driver, Windows-specific code.
*/
/*
* Copyright (C) 2014 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
#define LOG_GROUP LOG_GROUP_NET_FLT_DRV
//#define VBOXNETLWF_SYNC_SEND
#include <iprt/initterm.h>
#include <VBox/intnetinline.h>
/// @todo Not sure why, but can it help with build errors?
/* ntddk.h has a missing #pragma pack(), work around it
* see #ifdef VBOX_WITH_WORKAROUND_MISSING_PACK below for detail */
# endif
# include <ntddk.h>
# pragma warning(default : 4163)
# pragma pack()
# pragma warning(default : 4103)
# endif
# include <ndis.h>
#else
//# include <ntddk.h>
/* can include ndis.h right away */
# include <ndis.h>
#endif
#if 0
#endif
/** We have an entirely different structure than the one defined in VBoxNetFltCmn-win.h */
typedef struct VBOXNETFLTWIN
{
/** filter module context handle */
#define VBOXNETFLT_NO_PACKET_QUEUE
#define VBOXNETFLT_OS_SPECFIC 1
#include "VBoxNetFltInternal.h"
#include "VBoxNetLwf-win.h"
#include "VBoxNetCmn-win.h"
/* Forward declarations */
//FILTER_CANCEL_OID_REQUEST vboxNetLwfWinCancelOidRequest;
//FILTER_NET_PNP_EVENT vboxNetLwfWinPnPEvent;
typedef enum {
LwfState_Detached = 0,
LwfState_32BitHack = 0x7fffffff
/*
* 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 */
struct _VBOXNETLWF_MODULE;
typedef struct VBOXNETLWFGLOBALS
{
/** synch event used for device creation synchronization */
//KEVENT SynchEvent;
/** Device reference count */
//int cDeviceRefs;
/** ndis device */
/** device object */
/** our filter driver handle */
/** lock protecting the module list */
/** the head of module list */
/** IDC initialization state */
volatile uint32_t enmIdcState;
/** IDC init thread handle */
/**
* The (common) global data.
*/
static VBOXNETFLTGLOBALS g_VBoxNetFltGlobals;
/* win-specific global data */
typedef struct _VBOXNETLWF_MODULE {
/** Associated instance of NetFlt, one-to-one relationship */
/** Module state as described in http://msdn.microsoft.com/en-us/library/windows/hardware/ff550017(v=vs.85).aspx */
/** Mutex to prevent pausing while transmitting on behalf of NetFlt */
#ifdef VBOXNETLWF_SYNC_SEND
/** Event signalled when sending to the wire is complete */
/** Event signalled when NDIS returns our receive notification */
#else /* !VBOXNETLWF_SYNC_SEND */
/** Event signalled when all pending sends (both to wire and host) have completed */
/** Counter for pending sends (both to wire and host) */
#endif /* !VBOXNETLWF_SYNC_SEND */
/** Name of underlying adapter */
/** MAC address of underlying adapter */
/** Packet filter of underlying miniport */
/** Saved offload configuration */
/** the cloned request we have passed down */
/** true if the underlying miniport supplied offloading config */
bool fOffloadConfigValid;
/** true if the trunk expects data from us */
bool fActive;
typedef VBOXNETLWF_MODULE *PVBOXNETLWF_MODULE;
/*
* A structure to wrap OID requests in.
*/
typedef struct _VBOXNETLWF_OIDREQ {
typedef VBOXNETLWF_OIDREQ *PVBOXNETLWF_OIDREQ;
/* Forward declarations */
static int vboxNetLwfWinInitBase();
static int vboxNetLwfWinFini();
#ifdef DEBUG
{
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";
}
{
}
{
else
{
Log5((" null"));
Log5((" 802.3"));
Log5((" 802.3pq"));
Log5((" 802.3pq(oob)"));
Log5((" LLC"));
Log5(("\n"));
}
}
{
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";
}
{
switch (uOnOff)
{
case NDIS_OFFLOAD_NOT_SUPPORTED: return "off";
case NDIS_OFFLOAD_SUPPORTED: return "on";
}
return "unknown";
}
{
switch (uSupported)
{
case NDIS_OFFLOAD_NOT_SUPPORTED: return "not supported";
case NDIS_OFFLOAD_SUPPORTED: return "supported";
}
return "unknown";
}
{
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)));
}
{
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)));
}
{
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";
}
#else /* !DEBUG */
#define vboxNetLwfWinDumpFilterTypes(uFlags)
#define vboxNetLwfWinDumpOffloadSettings(p)
#define vboxNetLwfWinDumpSetOffloadSettings(p)
#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)
{
if (fSuccess)
else
}
else
{
}
return fSuccess;
}
{
}
static NDIS_STATUS vboxNetLwfWinSyncOidRequest(PVBOXNETLWF_MODULE pModuleCtx, PVBOXNETLWF_OIDREQ pRequest)
{
if (Status == NDIS_STATUS_PENDING)
{
}
return Status;
}
{
switch (pFrom->RequestType)
{
break;
case NdisRequestMethod:
pTo->DATA.METHOD_INFORMATION.OutputBufferLength = pFrom->DATA.METHOD_INFORMATION.OutputBufferLength;
break;
default:
}
}
{
&pClone);
if (Status == NDIS_STATUS_SUCCESS)
{
/* Save the pointer to the original */
/* 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);
{
ASMAtomicWriteU32((uint32_t*)&pModuleCtx->uPacketFilter, *(ULONG*)pOidRequest->DATA.SET_INFORMATION.InformationBuffer);
}
{
vboxNetLwfWinDumpSetOffloadSettings((PNDIS_OFFLOAD)pOidRequest->DATA.SET_INFORMATION.InformationBuffer);
}
if (Status != NDIS_STATUS_PENDING)
{
/* Synchronous completion */
}
/* In case of async completion we do the rest in vboxNetLwfWinOidRequestComplete() */
}
else
{
}
return Status;
}
{
if (pOriginal)
{
/* NDIS is supposed to serialize requests */
}
else
{
/* This is not a clone, we originated it */
}
}
{
}
#if 0
{
if (Status != NDIS_STATUS_SUCCESS)
{
Log((__FUNCTION__": vboxNetLwfWinSyncOidRequest(query, OID_GEN_CURRENT_PACKET_FILTER) failed with 0x%x\n", Status));
return FALSE;
}
{
Log((__FUNCTION__": 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));
}
return Status;
}
#endif
{
if (fPromisc)
if (Status != NDIS_STATUS_SUCCESS)
{
Log((__FUNCTION__": vboxNetLwfWinSyncOidRequest(set, OID_GEN_CURRENT_PACKET_FILTER, vvv below vvv) failed with 0x%x\n", Status));
}
return Status;
}
{
switch (pIrpSl->MajorFunction)
{
case IRP_MJ_DEVICE_CONTROL:
break;
case IRP_MJ_CREATE:
case IRP_MJ_CLEANUP:
case IRP_MJ_CLOSE:
break;
default:
Assert(0);
break;
}
return Status;
}
/** @todo So far we had no use for device, should we even bother to create it? */
{
//DeviceAttributes.ExtensionSize = sizeof(FILTER_DEVICE_EXTENSION);
#if 0
if (Status == NDIS_STATUS_SUCCESS)
{
}
#endif
return Status;
}
{
}
{
sizeof(VBOXNETLWF_MODULE),
if (!pModuleCtx)
return NDIS_STATUS_RESOURCES;
/* We use the miniport name to associate this filter module with the netflt instance */
TRUE);
if (rc != STATUS_SUCCESS)
{
Log(("ERROR! vboxNetLwfWinAttach: RtlUnicodeStringToAnsiString(%ls) failed with 0x%x\n",
NdisFreeMemory(pModuleCtx, 0, 0);
return NDIS_STATUS_FAILURE;
}
NdisMoveMemory(&pModuleCtx->MacAddr, pParameters->CurrentMacAddress, RT_MIN(sizeof(RTMAC), pParameters->MacAddressLength));
{
pModuleCtx->fOffloadConfigValid = true;
}
/* Insert into module chain */
/* Initialize transmission mutex and events */
#ifdef VBOXNETLWF_SYNC_SEND
#else /* !VBOXNETLWF_SYNC_SEND */
pModuleCtx->cPendingBuffers = 0;
#endif /* !VBOXNETLWF_SYNC_SEND */
/* Allocate buffer pools */
#ifndef VBOXNETLWF_SYNC_SEND
PoolParams.DataSize = 2048; /** @todo figure out the optimal size, use several pools if necessary, make configurable, etc */
#endif /* !VBOXNETLWF_SYNC_SEND */
if (!pModuleCtx->hPool)
{
NdisFreeMemory(pModuleCtx, 0, 0);
return NDIS_STATUS_RESOURCES;
}
Attributes.Flags = 0;
if (Status != NDIS_STATUS_SUCCESS)
{
NdisFreeMemory(pModuleCtx, 0, 0);
return NDIS_STATUS_RESOURCES;
}
/// @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.
return Status;
}
{
/* Remove from module chain */
{
/*
* Set hModuleCtx to null now in order to prevent filter restart,
* OID requests and other stuff associated with NetFlt deactivation.
*/
/* 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 */
}
/*
* 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().
*/
if (pModuleCtx->hPool)
{
}
NdisFreeMemory(hModuleCtx, 0, 0);
}
static NDIS_STATUS vboxNetLwfWinPause(IN NDIS_HANDLE hModuleCtx, IN PNDIS_FILTER_PAUSE_PARAMETERS pParameters)
{
/* Wait for pending send/indication operations to complete. */
#ifndef VBOXNETLWF_SYNC_SEND
#endif /* !VBOXNETLWF_SYNC_SEND */
return NDIS_STATUS_SUCCESS; /* Failure is not an option */
}
{
}
static NDIS_STATUS vboxNetLwfWinRestart(IN NDIS_HANDLE hModuleCtx, IN PNDIS_FILTER_RESTART_PARAMETERS pParameters)
{
#if 1
{
{
/* Disable offloading temporarily by indicating offload config change. */
/** @todo Be sure to revise this when implementing offloading support! */
}
else
{
/* The filter is inactive -- restore offloading configuration. */
}
}
#endif
return Status;
}
{
{
{
}
}
}
{
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.
*/
{
Log2(("NetLWF: %s (%d bytes), %RTmac => %RTmac, EthType=%s(0x%x)\n",
pHdr += sizeof(RTNETETHERHDR);
if (uEthType == RTNET_ETHERTYPE_VLAN)
{
}
switch (uEthType)
{
case RTNET_ETHERTYPE_IPV6:
pHdr += 40;
break;
case RTNET_ETHERTYPE_IPV4:
break;
case RTNET_ETHERTYPE_ARP:
{
case 1: /* ARP request */
Log2((" + ARP-REQ: who-has %RTnaipv4 tell %RTnaipv4\n",
break;
case 2: /* ARP reply */
Log2((" + ARP-RPL: %RTnaipv4 is-at %RTmac\n",
break;
default:
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 */
break;
case 51: /* IPv6 IPsec AH */
Log2((" + IPv6 IPsec AH: <not implemented>\n"));
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])
{
}
uProto = 0xFF;
break;
case 1: /* ICMP */
switch (pHdr[0])
{
case 0: Log2((" + ICMP: echo reply\n")); break;
}
uProto = 0xFF;
break;
case 6: /* TCP */
Log2((" + TCP: src=%d dst=%d seq=%x ack=%x\n",
uProto = 0xFF;
break;
case 17: /* UDP */
Log2((" + UDP: src=%d dst=%d\n",
uProto = 0xFF;
break;
default:
uProto = 0xFF;
break;
}
}
}
{
NdisFreeMemory(pSG, 0, 0);
}
{
cSegs++;
return cSegs;
}
{
#ifdef VBOXNETLWF_SYNC_SEND
while (pMdl)
{
}
#endif /* VBOXNETLWF_SYNC_SEND */
}
/** @todo
* 1) Copy data from SG to MDL (if we decide to complete asynchronously).
* 3) We always get a single segment from intnet. Simplify?
*/
{
#ifdef VBOXNETLWF_SYNC_SEND
if (!pMdl)
{
return NULL;
}
{
{
/* Tear down all MDL we chained so far */
return NULL;
}
}
0 /* ContextSize */,
0 /* ContextBackFill */,
pMdl,
0 /* DataOffset */,
if (pBufList)
{
/** @todo Do we need to initialize anything else? */
}
else
{
}
#else /* !VBOXNETLWF_SYNC_SEND */
0 /** @todo ContextSize */,
0 /** @todo ContextBackFill */);
NDIS_STATUS Status = NdisRetreatNetBufferDataStart(pBuffer, pSG->cbTotal, 0 /** @todo DataBackfill */, NULL);
if (Status == NDIS_STATUS_SUCCESS)
{
if (pDst)
{
{
}
/** @todo Do we need to initialize anything else? */
}
else
{
}
}
else
{
Log((__FUNCTION__": NdisRetreatNetBufferDataStart failed with 0x%x (size=%u)\n", Status, pSG->cbTotal));
}
#endif /* !VBOXNETLWF_SYNC_SEND */
return pBufList;
}
{
/* Allocate and initialize SG */
int rc = NDIS_STATUS_SUCCESS;
cSegs = 0;
{
if (!pSrc)
{
break;
}
if (uOffset)
{
uOffset = 0;
}
cSegs++;
}
if (RT_FAILURE(rc))
{
}
else
{
}
return pSG;
}
{
switch (pIndication->StatusCode)
{
break;
break;
}
}
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. */
{
return false;
}
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
*/
bool fDropIt = false;
bool fDontDrop = false;
int nLists = 0;
{
int nBuffers = 0;
nLists++;
{
nBuffers++;
if (pSG)
{
/* A bit paranoid, but we do not use any locks, so... */
fDropIt = true;
else
fDontDrop = true;
}
}
}
Log((__FUNCTION__": lists=%d drop=%s don't=%s\n", nLists, fDropIt ? "true":"false", fDontDrop ? "true":"false"));
}
{
}
VOID vboxNetLwfWinSendNetBufferLists(IN NDIS_HANDLE hModuleCtx, IN PNET_BUFFER_LIST pBufLists, IN NDIS_PORT_NUMBER nPort, IN ULONG fFlags)
{
{
{
{
if (pDropHead)
{
}
else
}
else
{
if (pPassHead)
{
}
else
}
}
if (pPassHead)
{
}
if (pDropHead)
{
}
}
else
{
{
}
}
}
VOID vboxNetLwfWinSendNetBufferListsComplete(IN NDIS_HANDLE hModuleCtx, IN PNET_BUFFER_LIST pBufLists, IN ULONG fFlags)
{
while (pList)
{
{
/* We allocated this NET_BUFFER_LIST, let's free it up */
/*
* 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.
*/
/* Unlink this list from the chain */
if (pPrevList)
else
Log((__FUNCTION__": our list %p, next=%p, previous=%p, head=%p\n", pList, pNextList, pPrevList, pBufLists));
#ifdef VBOXNETLWF_SYNC_SEND
#else /* !VBOXNETLWF_SYNC_SEND */
#endif /* !VBOXNETLWF_SYNC_SEND */
}
else
{
Log((__FUNCTION__": passing list %p, next=%p, previous=%p, head=%p\n", pList, pNextList, pPrevList, pBufLists));
}
}
if (pBufLists)
{
/* There are still lists remaining in the chain, pass'em up */
}
}
{
/// @todo Do we need loopback handling?
{
{
/* We do not own NBLs so we do not need to return them */
/* First we need to scan through the list to see if some packets must be dropped */
bool bDropIt = false;
{
bDropIt = true;
}
if (bDropIt)
{
/* Some NBLs must be dropped, indicate selectively one by one */
{
}
}
else
{
/* All NBLs must be indicated, do it in bulk. */
}
}
else
{
/* We collect dropped NBLs in a separate list in order to "return" them. */
{
{
if (nDrop++)
{
}
else
}
else
{
if (nPass++)
{
}
else
}
}
if (pPassHead)
{
}
if (pDropHead)
{
}
}
}
else
{
if ((fFlags & NDIS_RECEIVE_FLAGS_RESOURCES) == 0)
}
}
VOID vboxNetLwfWinReturnNetBufferLists(IN NDIS_HANDLE hModuleCtx, IN PNET_BUFFER_LIST pBufLists, IN ULONG fFlags)
{
/** @todo Move common part into a separate function to be used by vboxNetLwfWinSendNetBufferListsComplete() as well */
while (pList)
{
{
/* We allocated this NET_BUFFER_LIST, let's free it up */
/*
* 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.
*/
/* Unlink this list from the chain */
if (pPrevList)
else
#ifdef VBOXNETLWF_SYNC_SEND
#else /* !VBOXNETLWF_SYNC_SEND */
#endif /* !VBOXNETLWF_SYNC_SEND */
}
else
}
if (pBufLists)
{
/* There are still lists remaining in the chain, pass'em up */
}
}
{
{
}
else
{
}
return Status;
}
/**
* register the filter driver
*/
DECLHIDDEN(NDIS_STATUS) vboxNetLwfWinRegister(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegistryPathStr)
{
/* Mandatory functions */
/* Optional functions, non changeble at run-time */
//FChars.CancelOidRequestHandler = vboxNetLwfWinCancelOidRequest;
//FChars.NetPnPEventHandler = vboxNetLwfWinPnPEvent;
/* Optional functions */
Log(("vboxNetLwfWinRegister: registering filter driver...\n"));
&FChars,
if (Status == STATUS_SUCCESS)
{
Log(("vboxNetLwfWinRegister: successfully registered filter driver; registering device...\n"));
}
else
{
}
return Status;
}
static int vboxNetLwfWinStartInitIdcThread()
{
int rc = VERR_INVALID_STATE;
if (ASMAtomicCmpXchgU32(&g_VBoxNetLwfGlobals.enmIdcState, LwfIdcState_Connecting, LwfIdcState_Disconnected))
{
NULL,
NULL,
NULL,
if (Status != STATUS_SUCCESS)
{
/*
* We failed to init IDC and there will be no second chance.
*/
}
}
return rc;
}
static void vboxNetLwfWinStopInitIdcThread()
{
}
{
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();
if (RT_SUCCESS(rc))
{
/*
* 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.
*/
if (Status == NDIS_STATUS_SUCCESS)
{
Log(("NETLWF: started successfully\n"));
return STATUS_SUCCESS;
}
}
else
{
}
return Status;
}
{
}
{
switch (enmState)
{
case LwfIdcState_Disconnected: return "Disconnected";
case LwfIdcState_Connecting: return "Connecting";
case LwfIdcState_Connected: return "Connected";
case LwfIdcState_Stopping: return "Stopping";
}
return "Unknown";
}
{
int rc;
{
if (RT_SUCCESS(rc))
{
{
/* The state has been changed (the only valid transition is to "Stopping"), undo init */
Log((__FUNCTION__": state change (Connecting -> %s) while initializing IDC, deleted IDC, rc=0x%x\n",
}
else
{
}
}
else
{
}
}
}
static int vboxNetLwfWinTryFiniIdc()
{
int rc = VINF_SUCCESS;
Log((__FUNCTION__": 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 */
break;
case LwfIdcState_Connecting:
/* the worker thread is running, let's wait for it to stop */
if (Status == STATUS_SUCCESS)
{
}
else
{
}
break;
case LwfIdcState_Connected:
/* the worker succeeded in IDC init and terminated */
break;
}
return rc;
}
static void vboxNetLwfWinFiniBase()
{
/*
* Undo the work done during start (in reverse order).
*/
RTR0Term();
}
static int vboxNetLwfWinInitBase()
{
if (!RT_SUCCESS(rc))
return rc;
if (!RT_SUCCESS(rc))
RTR0Term();
return rc;
}
static int vboxNetLwfWinFini()
{
int rc = vboxNetLwfWinTryFiniIdc();
if (RT_SUCCESS(rc))
{
}
return rc;
}
/*
*
* The OS specific interface definition
*
*/
{
LogFlow(("<=="__FUNCTION__": return %RTbool\n", !ASMAtomicUoReadBool(&pThis->fDisconnectedFromHost)));
/* AttachToInterface true if disconnected */
}
{
int rc = VINF_SUCCESS;
if (!pModule)
{
return VERR_INTERNAL_ERROR;
}
/* Prevent going into "paused" state until all transmissions have been completed. */
/* Ignore all sends if the stack is paused or being paused, etc... */
if (!vboxNetLwfWinIsRunning(pModule))
{
return VINF_SUCCESS;
}
/*
* 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.
* 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
#endif /* VBOXNETLWF_SYNC_SEND */
if (fDst & INTNETTRUNKDIR_WIRE)
{
if (pBufList)
{
#ifdef VBOXNETLWF_SYNC_SEND
#else /* !VBOXNETLWF_SYNC_SEND */
#endif /* !VBOXNETLWF_SYNC_SEND */
NdisFSendNetBufferLists(pModule->hFilter, pBufList, NDIS_DEFAULT_PORT_NUMBER, 0); /** @todo sendFlags! */
}
}
if (fDst & INTNETTRUNKDIR_HOST)
{
if (pBufList)
{
#ifdef VBOXNETLWF_SYNC_SEND
#else /* !VBOXNETLWF_SYNC_SEND */
#endif /* !VBOXNETLWF_SYNC_SEND */
}
}
#ifdef VBOXNETLWF_SYNC_SEND
if (nEvents)
{
NTSTATUS Status = KeWaitForMultipleObjects(nEvents, aEvents, WaitAll, Executive, KernelMode, FALSE, &timeout, NULL);
if (Status != STATUS_SUCCESS)
{
if (Status == STATUS_TIMEOUT)
rc = VERR_TIMEOUT;
else
}
}
#endif /* VBOXNETLWF_SYNC_SEND */
return rc;
}
{
LogFlow(("==>"__FUNCTION__": instance=%p module=%p fActive=%RTbool\n", pThis, pModuleCtx, fActive));
if (!pModuleCtx)
{
return;
}
if (fOldActive != fActive)
{
/// @todo Shouldn't we wait for traffic to cease here? Probably not.
}
else
}
{
return VINF_SUCCESS;
}
{
return VINF_SUCCESS;
}
{
/* Technically it is possible that the module has already been gone by now. */
if (pModuleCtx)
{
}
}
{
if (pThis->pSwitchPort
{
}
}
{
{
{
return VINF_SUCCESS;
}
}
return VERR_INTNET_FLT_IF_NOT_FOUND;
}
{
return VINF_SUCCESS;
}
{
}
{
/* Nothing to do */
return VINF_SUCCESS;
}
{
/* Nothing to do */
return VINF_SUCCESS;
}