diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-06 03:01:46 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-06 03:01:46 +0000 |
commit | f8fe689a81f906d1b91bb3220acde2a4ecb14c5b (patch) | |
tree | 26484e9d7e2c67806c2d1760196ff01aaa858e8c /src/VBox/HostDrivers/VBoxNetFlt/win | |
parent | Initial commit. (diff) | |
download | virtualbox-upstream.tar.xz virtualbox-upstream.zip |
Adding upstream version 6.0.4-dfsg.upstream/6.0.4-dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/HostDrivers/VBoxNetFlt/win')
33 files changed, 17262 insertions, 0 deletions
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/Makefile.kup b/src/VBox/HostDrivers/VBoxNetFlt/win/Makefile.kup new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/VBox/HostDrivers/VBoxNetFlt/win/Makefile.kup diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/cfg/Makefile.kup b/src/VBox/HostDrivers/VBoxNetFlt/win/cfg/Makefile.kup new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/VBox/HostDrivers/VBoxNetFlt/win/cfg/Makefile.kup diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/cfg/VBoxNetCfg.cpp b/src/VBox/HostDrivers/VBoxNetFlt/win/cfg/VBoxNetCfg.cpp new file mode 100644 index 00000000..7a3a2373 --- /dev/null +++ b/src/VBox/HostDrivers/VBoxNetFlt/win/cfg/VBoxNetCfg.cpp @@ -0,0 +1,3677 @@ +/* $Id: VBoxNetCfg.cpp $ */ +/** @file + * VBoxNetCfg.cpp - Network Configuration API. + */ +/* + * Copyright (C) 2011-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * 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. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE 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. + */ +#include "VBox/VBoxNetCfg-win.h" +#include "VBox/VBoxDrvCfg-win.h" + +#define _WIN32_DCOM + +#include <devguid.h> +#include <stdio.h> +#include <regstr.h> +#include <iprt/win/shlobj.h> +#include <cfgmgr32.h> +#include <tchar.h> +#include <iprt/win/objbase.h> + +#include <crtdbg.h> +#include <stdlib.h> +#include <string.h> + +#include <Wbemidl.h> +#include <comdef.h> + +#include <iprt/win/winsock2.h> +#include <iprt/win/ws2tcpip.h> +#include <ws2ipdef.h> +#include <iprt/win/netioapi.h> +#include <iprt/win/iphlpapi.h> + + +#ifndef Assert /** @todo r=bird: where would this be defined? */ +//# ifdef DEBUG +//# define Assert(_expr) assert(_expr) +//# else +//# define Assert(_expr) do{ }while (0) +//# endif +# define Assert _ASSERT +# define AssertMsg(expr, msg) do{}while (0) +#endif +static LOG_ROUTINE g_Logger = NULL; + +static VOID DoLogging(LPCSTR szString, ...); +#define NonStandardLog DoLogging +#define NonStandardLogFlow(x) DoLogging x + +#define DbgLog /** @todo r=bird: What does this do? */ + +#define VBOX_NETCFG_LOCK_TIME_OUT 5000 /** @todo r=bird: What does this do? */ + +#define VBOXNETCFGWIN_NETADP_ID L"sun_VBoxNetAdp" + +/* +* Wrappers for HelpAPI functions +*/ +typedef void FNINITIALIZEIPINTERFACEENTRY( _Inout_ PMIB_IPINTERFACE_ROW row); +typedef FNINITIALIZEIPINTERFACEENTRY *PFNINITIALIZEIPINTERFACEENTRY; + +typedef NETIOAPI_API FNGETIPINTERFACEENTRY( _Inout_ PMIB_IPINTERFACE_ROW row); +typedef FNGETIPINTERFACEENTRY *PFNGETIPINTERFACEENTRY; + +typedef NETIOAPI_API FNSETIPINTERFACEENTRY( _Inout_ PMIB_IPINTERFACE_ROW row); +typedef FNSETIPINTERFACEENTRY *PFNSETIPINTERFACEENTRY; + +static PFNINITIALIZEIPINTERFACEENTRY g_pfnInitializeIpInterfaceEntry = NULL; +static PFNGETIPINTERFACEENTRY g_pfnGetIpInterfaceEntry = NULL; +static PFNSETIPINTERFACEENTRY g_pfnSetIpInterfaceEntry = NULL; + + +/* +* Forward declaration for using vboxNetCfgWinSetupMetric() +*/ +HRESULT vboxNetCfgWinSetupMetric(IN NET_LUID* pLuid); +HRESULT vboxNetCfgWinGetInterfaceLUID(IN HKEY hKey, OUT NET_LUID* pLUID); + + +/* + * For some weird reason we do not want to use IPRT here, hence the following + * function provides a replacement for BstrFmt. + */ +static bstr_t bstr_printf(const char *cszFmt, ...) +{ + char szBuffer[4096]; + szBuffer[sizeof(szBuffer) - 1] = 0; /* Make sure the string will be null-terminated */ + va_list va; + va_start(va, cszFmt); + _vsnprintf(szBuffer, sizeof(szBuffer) - 1, cszFmt, va); + va_end(va); + return bstr_t(szBuffer); +} + +static HRESULT vboxNetCfgWinINetCfgLock(IN INetCfg *pNetCfg, + IN LPCWSTR pszwClientDescription, + IN DWORD cmsTimeout, + OUT LPWSTR *ppszwClientDescription) +{ + INetCfgLock *pLock; + HRESULT hr = pNetCfg->QueryInterface(IID_INetCfgLock, (PVOID*)&pLock); + if (FAILED(hr)) + { + NonStandardLogFlow(("QueryInterface failed, hr (0x%x)\n", hr)); + return hr; + } + + hr = pLock->AcquireWriteLock(cmsTimeout, pszwClientDescription, ppszwClientDescription); + if (hr == S_FALSE) + { + NonStandardLogFlow(("Write lock busy\n")); + } + else if (FAILED(hr)) + { + NonStandardLogFlow(("AcquireWriteLock failed, hr (0x%x)\n", hr)); + } + + pLock->Release(); + return hr; +} + +static HRESULT vboxNetCfgWinINetCfgUnlock(IN INetCfg *pNetCfg) +{ + INetCfgLock *pLock; + HRESULT hr = pNetCfg->QueryInterface(IID_INetCfgLock, (PVOID*)&pLock); + if (FAILED(hr)) + { + NonStandardLogFlow(("QueryInterface failed, hr (0x%x)\n", hr)); + return hr; + } + + hr = pLock->ReleaseWriteLock(); + if (FAILED(hr)) + NonStandardLogFlow(("ReleaseWriteLock failed, hr (0x%x)\n", hr)); + + pLock->Release(); + return hr; +} + +VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinQueryINetCfg(OUT INetCfg **ppNetCfg, + IN BOOL fGetWriteLock, + IN LPCWSTR pszwClientDescription, + IN DWORD cmsTimeout, + OUT LPWSTR *ppszwClientDescription) +{ + INetCfg *pNetCfg; + HRESULT hr = CoCreateInstance(CLSID_CNetCfg, NULL, CLSCTX_INPROC_SERVER, IID_INetCfg, (PVOID*)&pNetCfg); + if (FAILED(hr)) + { + NonStandardLogFlow(("CoCreateInstance failed, hr (0x%x)\n", hr)); + return hr; + } + + if (fGetWriteLock) + { + hr = vboxNetCfgWinINetCfgLock(pNetCfg, pszwClientDescription, cmsTimeout, ppszwClientDescription); + if (hr == S_FALSE) + { + NonStandardLogFlow(("Write lock is busy\n", hr)); + hr = NETCFG_E_NO_WRITE_LOCK; + } + } + + if (SUCCEEDED(hr)) + { + hr = pNetCfg->Initialize(NULL); + if (SUCCEEDED(hr)) + { + *ppNetCfg = pNetCfg; + return S_OK; + } + else + NonStandardLogFlow(("Initialize failed, hr (0x%x)\n", hr)); + } + + pNetCfg->Release(); + return hr; +} + +VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinReleaseINetCfg(IN INetCfg *pNetCfg, IN BOOL fHasWriteLock) +{ + if (!pNetCfg) /* If network config has been released already, just bail out. */ + { + NonStandardLogFlow(("Warning: No network config given but write lock is set to TRUE\n")); + return S_OK; + } + + HRESULT hr = pNetCfg->Uninitialize(); + if (FAILED(hr)) + { + NonStandardLogFlow(("Uninitialize failed, hr (0x%x)\n", hr)); + /* Try to release the write lock below. */ + } + + if (fHasWriteLock) + { + HRESULT hr2 = vboxNetCfgWinINetCfgUnlock(pNetCfg); + if (FAILED(hr2)) + NonStandardLogFlow(("vboxNetCfgWinINetCfgUnlock failed, hr (0x%x)\n", hr2)); + if (SUCCEEDED(hr)) + hr = hr2; + } + + pNetCfg->Release(); + return hr; +} + +static HRESULT vboxNetCfgWinGetComponentByGuidEnum(IEnumNetCfgComponent *pEnumNcc, + IN const GUID *pGuid, + OUT INetCfgComponent **ppNcc) +{ + HRESULT hr = pEnumNcc->Reset(); + if (FAILED(hr)) + { + NonStandardLogFlow(("Reset failed, hr (0x%x)\n", hr)); + return hr; + } + + INetCfgComponent *pNcc; + while ((hr = pEnumNcc->Next(1, &pNcc, NULL)) == S_OK) + { + ULONG uComponentStatus; + hr = pNcc->GetDeviceStatus(&uComponentStatus); + if (SUCCEEDED(hr)) + { + if (uComponentStatus == 0) + { + GUID NccGuid; + hr = pNcc->GetInstanceGuid(&NccGuid); + + if (SUCCEEDED(hr)) + { + if (NccGuid == *pGuid) + { + /* found the needed device */ + *ppNcc = pNcc; + break; + } + } + else + NonStandardLogFlow(("GetInstanceGuid failed, hr (0x%x)\n", hr)); + } + } + + pNcc->Release(); + } + return hr; +} + +VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinGetComponentByGuid(IN INetCfg *pNc, + IN const GUID *pguidClass, + IN const GUID * pComponentGuid, + OUT INetCfgComponent **ppncc) +{ + IEnumNetCfgComponent *pEnumNcc; + HRESULT hr = pNc->EnumComponents(pguidClass, &pEnumNcc); + + if (SUCCEEDED(hr)) + { + hr = vboxNetCfgWinGetComponentByGuidEnum(pEnumNcc, pComponentGuid, ppncc); + if (hr == S_FALSE) + { + NonStandardLogFlow(("Component not found\n")); + } + else if (FAILED(hr)) + { + NonStandardLogFlow(("vboxNetCfgWinGetComponentByGuidEnum failed, hr (0x%x)\n", hr)); + } + pEnumNcc->Release(); + } + else + NonStandardLogFlow(("EnumComponents failed, hr (0x%x)\n", hr)); + return hr; +} + +static HRESULT vboxNetCfgWinQueryInstaller(IN INetCfg *pNetCfg, IN const GUID *pguidClass, INetCfgClassSetup **ppSetup) +{ + HRESULT hr = pNetCfg->QueryNetCfgClass(pguidClass, IID_INetCfgClassSetup, (void**)ppSetup); + if (FAILED(hr)) + NonStandardLogFlow(("QueryNetCfgClass failed, hr (0x%x)\n", hr)); + return hr; +} + +VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinInstallComponent(IN INetCfg *pNetCfg, IN LPCWSTR pszwComponentId, IN const GUID *pguidClass, + OUT INetCfgComponent **ppComponent) +{ + INetCfgClassSetup *pSetup; + HRESULT hr = vboxNetCfgWinQueryInstaller(pNetCfg, pguidClass, &pSetup); + if (FAILED(hr)) + { + NonStandardLogFlow(("vboxNetCfgWinQueryInstaller failed, hr (0x%x)\n", hr)); + return hr; + } + + OBO_TOKEN Token; + ZeroMemory(&Token, sizeof (Token)); + Token.Type = OBO_USER; + INetCfgComponent* pTempComponent = NULL; + + hr = pSetup->Install(pszwComponentId, &Token, + 0, /* IN DWORD dwSetupFlags */ + 0, /* IN DWORD dwUpgradeFromBuildNo */ + NULL, /* IN LPCWSTR pszwAnswerFile */ + NULL, /* IN LPCWSTR pszwAnswerSections */ + &pTempComponent); + if (SUCCEEDED(hr)) + { + if (pTempComponent != NULL) + { + HKEY hkey = (HKEY)INVALID_HANDLE_VALUE; + HRESULT res; + + /* + * Set default metric value of interface to fix multicast issue + * See @bugref{6379} for details. + */ + res = pTempComponent->OpenParamKey(&hkey); + + /* Set default metric value for host-only interface only */ + if ( SUCCEEDED(res) + && hkey != INVALID_HANDLE_VALUE + && wcsnicmp(pszwComponentId, VBOXNETCFGWIN_NETADP_ID, 256) == 0) + { + NET_LUID luid; + res = vboxNetCfgWinGetInterfaceLUID(hkey, &luid); + + /* Close the key as soon as possible. See @bugref{7973}. */ + RegCloseKey (hkey); + hkey = (HKEY)INVALID_HANDLE_VALUE; + + if (FAILED(res)) + { + /* + * The setting of Metric is not very important functionality, + * So we will not break installation process due to this error. + */ + NonStandardLogFlow(("VBoxNetCfgWinInstallComponent Warning! " + "vboxNetCfgWinGetInterfaceLUID failed, default metric " + "for new interface will not be set, hr (0x%x)\n", res)); + } + else + { + res = vboxNetCfgWinSetupMetric(&luid); + if (FAILED(res)) + { + /* + * The setting of Metric is not very important functionality, + * So we will not break installation process due to this error. + */ + NonStandardLogFlow(("VBoxNetCfgWinInstallComponent Warning! " + "vboxNetCfgWinSetupMetric failed, default metric " + "for new interface will not be set, hr (0x%x)\n", res)); + } + } + } + if (hkey != INVALID_HANDLE_VALUE) + { + RegCloseKey (hkey); + hkey = (HKEY)INVALID_HANDLE_VALUE; + } + if (ppComponent != NULL) + *ppComponent = pTempComponent; + else + pTempComponent->Release(); + } + + /* ignore the apply failure */ + HRESULT tmpHr = pNetCfg->Apply(); + Assert(tmpHr == S_OK); + if (tmpHr != S_OK) + NonStandardLogFlow(("Apply failed, hr (0x%x)\n", tmpHr)); + } + else + NonStandardLogFlow(("Install failed, hr (0x%x)\n", hr)); + + pSetup->Release(); + return hr; +} + +static HRESULT vboxNetCfgWinInstallInfAndComponent(IN INetCfg *pNetCfg, IN LPCWSTR pszwComponentId, IN const GUID *pguidClass, + IN LPCWSTR const *apInfPaths, IN UINT cInfPaths, + OUT INetCfgComponent **ppComponent) +{ + HRESULT hr = S_OK; + UINT cFilesProcessed = 0; + + NonStandardLogFlow(("Installing %u INF files ...\n", cInfPaths)); + + for (; cFilesProcessed < cInfPaths; cFilesProcessed++) + { + NonStandardLogFlow(("Installing INF file \"%ws\" ...\n", apInfPaths[cFilesProcessed])); + hr = VBoxDrvCfgInfInstall(apInfPaths[cFilesProcessed]); + if (FAILED(hr)) + { + NonStandardLogFlow(("VBoxNetCfgWinInfInstall failed, hr (0x%x)\n", hr)); + break; + } + } + + if (SUCCEEDED(hr)) + { + hr = VBoxNetCfgWinInstallComponent(pNetCfg, pszwComponentId, pguidClass, ppComponent); + if (FAILED(hr)) + NonStandardLogFlow(("VBoxNetCfgWinInstallComponent failed, hr (0x%x)\n", hr)); + } + + if (FAILED(hr)) + { + NonStandardLogFlow(("Installation failed, rolling back installation set ...\n")); + + do + { + HRESULT hr2 = VBoxDrvCfgInfUninstall(apInfPaths[cFilesProcessed], 0); + if (FAILED(hr2)) + NonStandardLogFlow(("VBoxDrvCfgInfUninstall failed, hr (0x%x)\n", hr2)); + /* Keep going. */ + if (!cFilesProcessed) + break; + } while (cFilesProcessed--); + + NonStandardLogFlow(("Rollback complete\n")); + } + + return hr; +} + +VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinUninstallComponent(IN INetCfg *pNetCfg, IN INetCfgComponent *pComponent) +{ + GUID GuidClass; + HRESULT hr = pComponent->GetClassGuid(&GuidClass); + if (FAILED(hr)) + { + NonStandardLogFlow(("GetClassGuid failed, hr (0x%x)\n", hr)); + return hr; + } + + INetCfgClassSetup *pSetup = NULL; + hr = vboxNetCfgWinQueryInstaller(pNetCfg, &GuidClass, &pSetup); + if (FAILED(hr)) + { + NonStandardLogFlow(("vboxNetCfgWinQueryInstaller failed, hr (0x%x)\n", hr)); + return hr; + } + + OBO_TOKEN Token; + ZeroMemory(&Token, sizeof(Token)); + Token.Type = OBO_USER; + + hr = pSetup->DeInstall(pComponent, &Token, NULL /* OUT LPWSTR *pmszwRefs */); + if (SUCCEEDED(hr)) + { + hr = pNetCfg->Apply(); + if (FAILED(hr)) + NonStandardLogFlow(("Apply failed, hr (0x%x)\n", hr)); + } + else + NonStandardLogFlow(("DeInstall failed, hr (0x%x)\n", hr)); + + if (pSetup) + pSetup->Release(); + return hr; +} + +typedef BOOL (*VBOXNETCFGWIN_NETCFGENUM_CALLBACK) (IN INetCfg *pNetCfg, IN INetCfgComponent *pNetCfgComponent, PVOID pContext); + +static HRESULT vboxNetCfgWinEnumNetCfgComponents(IN INetCfg *pNetCfg, + IN const GUID *pguidClass, + VBOXNETCFGWIN_NETCFGENUM_CALLBACK callback, + PVOID pContext) +{ + IEnumNetCfgComponent *pEnumComponent; + HRESULT hr = pNetCfg->EnumComponents(pguidClass, &pEnumComponent); + if (SUCCEEDED(hr)) + { + INetCfgComponent *pNetCfgComponent; + hr = pEnumComponent->Reset(); + do + { + hr = pEnumComponent->Next(1, &pNetCfgComponent, NULL); + if (hr == S_OK) + { +// ULONG uComponentStatus; +// hr = pNcc->GetDeviceStatus(&uComponentStatus); +// if (SUCCEEDED(hr)) + BOOL fResult = FALSE; + if (pNetCfgComponent) + { + if (pContext) + fResult = callback(pNetCfg, pNetCfgComponent, pContext); + pNetCfgComponent->Release(); + } + + if (!fResult) + break; + } + else + { + if (hr == S_FALSE) + { + hr = S_OK; + } + else + NonStandardLogFlow(("Next failed, hr (0x%x)\n", hr)); + break; + } + } while (true); + pEnumComponent->Release(); + } + return hr; +} + +/* + * Forward declarations of functions used in vboxNetCfgWinRemoveAllNetDevicesOfIdCallback. + */ +VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinGenHostonlyConnectionName(PCWSTR DevName, WCHAR *pBuf, PULONG pcbBuf); +VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinRenameConnection(LPWSTR pGuid, PCWSTR NewName); + +static BOOL vboxNetCfgWinRemoveAllNetDevicesOfIdCallback(HDEVINFO hDevInfo, PSP_DEVINFO_DATA pDev, PVOID pvContext) +{ + RT_NOREF1(pvContext); + SP_REMOVEDEVICE_PARAMS rmdParams; + memset(&rmdParams, 0, sizeof(SP_REMOVEDEVICE_PARAMS)); + rmdParams.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER); + rmdParams.ClassInstallHeader.InstallFunction = DIF_REMOVE; + rmdParams.Scope = DI_REMOVEDEVICE_GLOBAL; + + if (SetupDiSetClassInstallParams(hDevInfo,pDev, + &rmdParams.ClassInstallHeader, sizeof(rmdParams))) + { + if (SetupDiSetSelectedDevice(hDevInfo, pDev)) + { +#ifndef VBOXNETCFG_DELAYEDRENAME + /* Figure out NetCfgInstanceId. */ + HKEY hKey = SetupDiOpenDevRegKey(hDevInfo, + pDev, + DICS_FLAG_GLOBAL, + 0, + DIREG_DRV, + KEY_READ); + if (hKey == INVALID_HANDLE_VALUE) + { + NonStandardLogFlow(("vboxNetCfgWinRemoveAllNetDevicesOfIdCallback: SetupDiOpenDevRegKey failed with error %ld\n", + GetLastError())); + } + else + { + WCHAR wszCfgGuidString[50] = { L'' }; + DWORD cbSize = sizeof(wszCfgGuidString); + DWORD dwValueType; + DWORD ret = RegQueryValueExW(hKey, L"NetCfgInstanceId", NULL, + &dwValueType, (LPBYTE)wszCfgGuidString, &cbSize); + if (ret == ERROR_SUCCESS) + { + NonStandardLogFlow(("vboxNetCfgWinRemoveAllNetDevicesOfIdCallback: Processing device ID \"%S\"\n", + wszCfgGuidString)); + + /* Figure out device name. */ + WCHAR wszDevName[256], wszTempName[256]; + ULONG cbName = sizeof(wszTempName); + + if (SetupDiGetDeviceRegistryPropertyW(hDevInfo, pDev, + SPDRP_FRIENDLYNAME, /* IN DWORD Property,*/ + NULL, /* OUT PDWORD PropertyRegDataType, OPTIONAL*/ + (PBYTE)wszDevName, /* OUT PBYTE PropertyBuffer,*/ + sizeof(wszDevName), /* IN DWORD PropertyBufferSize,*/ + NULL /* OUT PDWORD RequiredSize OPTIONAL*/)) + { + /* + * Rename the connection before removing the device. This will + * hopefully prevent an error when we will be attempting + * to rename a newly created connection (see @bugref{6740}). + */ + HRESULT hr = VBoxNetCfgWinGenHostonlyConnectionName(wszDevName, wszTempName, &cbName); + wcscat_s(wszTempName, sizeof(wszTempName), L" removed"); + if (SUCCEEDED(hr)) + hr = VBoxNetCfgWinRenameConnection(wszCfgGuidString, wszTempName); + //NonStandardLogFlow(("VBoxNetCfgWinRenameConnection(%S,%S) => 0x%x\n", pWCfgGuidString, TempName, hr_tmp)); + } + else + { + NonStandardLogFlow(("vboxNetCfgWinRemoveAllNetDevicesOfIdCallback: Failed to get friendly name for device \"%S\"\n", + wszCfgGuidString)); + } + } + else + { + NonStandardLogFlow(("vboxNetCfgWinRemoveAllNetDevicesOfIdCallback: Querying instance ID failed with %d\n", + ret)); + } + + RegCloseKey(hKey); + } +#endif /* VBOXNETCFG_DELAYEDRENAME */ + + if (SetupDiCallClassInstaller(DIF_REMOVE, hDevInfo, pDev)) + { + SP_DEVINSTALL_PARAMS devParams; + memset(&devParams, 0, sizeof(SP_DEVINSTALL_PARAMS)); + devParams.cbSize = sizeof(devParams); + + if (SetupDiGetDeviceInstallParams(hDevInfo, pDev, &devParams)) + { + if ( (devParams.Flags & DI_NEEDRESTART) + || (devParams.Flags & DI_NEEDREBOOT)) + { + NonStandardLog(("vboxNetCfgWinRemoveAllNetDevicesOfIdCallback: A reboot is required\n")); + } + } + else + NonStandardLogFlow(("vboxNetCfgWinRemoveAllNetDevicesOfIdCallback: SetupDiGetDeviceInstallParams failed with %ld\n", + GetLastError())); + } + else + NonStandardLogFlow(("vboxNetCfgWinRemoveAllNetDevicesOfIdCallback: SetupDiCallClassInstaller failed with %ld\n", + GetLastError())); + } + else + NonStandardLogFlow(("vboxNetCfgWinRemoveAllNetDevicesOfIdCallback: SetupDiSetSelectedDevice failed with %ld\n", + GetLastError())); + } + else + NonStandardLogFlow(("vboxNetCfgWinRemoveAllNetDevicesOfIdCallback: SetupDiSetClassInstallParams failed with %ld\n", + GetLastError())); + + /* Continue enumeration. */ + return TRUE; +} + +typedef struct VBOXNECTFGWINPROPCHANGE +{ + VBOXNECTFGWINPROPCHANGE_TYPE enmPcType; + HRESULT hr; +} VBOXNECTFGWINPROPCHANGE ,*PVBOXNECTFGWINPROPCHANGE; + +static BOOL vboxNetCfgWinPropChangeAllNetDevicesOfIdCallback(HDEVINFO hDevInfo, PSP_DEVINFO_DATA pDev, PVOID pContext) +{ + PVBOXNECTFGWINPROPCHANGE pPc = (PVBOXNECTFGWINPROPCHANGE)pContext; + + SP_PROPCHANGE_PARAMS PcParams; + memset (&PcParams, 0, sizeof (PcParams)); + PcParams.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER); + PcParams.ClassInstallHeader.InstallFunction = DIF_PROPERTYCHANGE; + PcParams.Scope = DICS_FLAG_GLOBAL; + + switch(pPc->enmPcType) + { + case VBOXNECTFGWINPROPCHANGE_TYPE_DISABLE: + PcParams.StateChange = DICS_DISABLE; + NonStandardLogFlow(("vboxNetCfgWinPropChangeAllNetDevicesOfIdCallback: Change type (DICS_DISABLE): %d\n", pPc->enmPcType)); + break; + case VBOXNECTFGWINPROPCHANGE_TYPE_ENABLE: + PcParams.StateChange = DICS_ENABLE; + NonStandardLogFlow(("vboxNetCfgWinPropChangeAllNetDevicesOfIdCallback: Change type (DICS_ENABLE): %d\n", pPc->enmPcType)); + break; + default: + NonStandardLogFlow(("vboxNetCfgWinPropChangeAllNetDevicesOfIdCallback: Unexpected prop change type: %d\n", pPc->enmPcType)); + pPc->hr = E_INVALIDARG; + return FALSE; + } + + if (SetupDiSetClassInstallParams(hDevInfo, pDev, &PcParams.ClassInstallHeader, sizeof(PcParams))) + { + if (SetupDiSetSelectedDevice(hDevInfo, pDev)) + { + if (SetupDiCallClassInstaller(DIF_PROPERTYCHANGE, hDevInfo, pDev)) + { + SP_DEVINSTALL_PARAMS devParams; + devParams.cbSize = sizeof(devParams); + if (SetupDiGetDeviceInstallParams(hDevInfo,pDev,&devParams)) + { + if ( (devParams.Flags & DI_NEEDRESTART) + || (devParams.Flags & DI_NEEDREBOOT)) + { + NonStandardLog(("vboxNetCfgWinPropChangeAllNetDevicesOfIdCallback: A reboot is required\n")); + } + } + else + NonStandardLogFlow(("vboxNetCfgWinPropChangeAllNetDevicesOfIdCallback: SetupDiGetDeviceInstallParams failed with %ld\n", + GetLastError())); + } + else + NonStandardLogFlow(("vboxNetCfgWinPropChangeAllNetDevicesOfIdCallback: SetupDiCallClassInstaller failed with %ld\n", + GetLastError())); + } + else + NonStandardLogFlow(("SetupDiSetSelectedDevice failed with %ld\n", GetLastError())); + } + else + NonStandardLogFlow(("SetupDiSetClassInstallParams failed with %ld\n", GetLastError())); + + /* Continue enumeration. */ + return TRUE; +} + +typedef BOOL (*PFNVBOXNETCFGWINNETENUMCALLBACK)(HDEVINFO hDevInfo, PSP_DEVINFO_DATA pDev, PVOID pContext); +VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinEnumNetDevices(LPCWSTR pwszPnPId, + PFNVBOXNETCFGWINNETENUMCALLBACK pfnCallback, PVOID pvContext) +{ + NonStandardLogFlow(("VBoxNetCfgWinEnumNetDevices: Searching for: %S\n", pwszPnPId)); + + HRESULT hr; + HDEVINFO hDevInfo = SetupDiGetClassDevsExW(&GUID_DEVCLASS_NET, + NULL, /* IN PCTSTR Enumerator, OPTIONAL */ + NULL, /* IN HWND hwndParent, OPTIONAL */ + DIGCF_PRESENT, /* IN DWORD Flags,*/ + NULL, /* IN HDEVINFO DeviceInfoSet, OPTIONAL */ + NULL, /* IN PCTSTR MachineName, OPTIONAL */ + NULL /* IN PVOID Reserved */); + if (hDevInfo != INVALID_HANDLE_VALUE) + { + DWORD winEr = NO_ERROR; + + DWORD dwDevId = 0; + size_t cPnPId = wcslen(pwszPnPId); + + PBYTE pBuffer = NULL; + + for (;;) + { + SP_DEVINFO_DATA Dev; + memset(&Dev, 0, sizeof(SP_DEVINFO_DATA)); + Dev.cbSize = sizeof(SP_DEVINFO_DATA); + + if (!SetupDiEnumDeviceInfo(hDevInfo, dwDevId, &Dev)) + { + winEr = GetLastError(); + if (winEr == ERROR_NO_MORE_ITEMS) + winEr = ERROR_SUCCESS; + break; + } + + NonStandardLogFlow(("VBoxNetCfgWinEnumNetDevices: Enumerating device %ld ... \n", dwDevId)); + dwDevId++; + + if (pBuffer) + free(pBuffer); + pBuffer = NULL; + DWORD cbBuffer = 0; + DWORD cbRequired = 0; + + if (!SetupDiGetDeviceRegistryPropertyW(hDevInfo, &Dev, + SPDRP_HARDWAREID, /* IN DWORD Property */ + NULL, /* OUT PDWORD PropertyRegDataType OPTIONAL */ + pBuffer, /* OUT PBYTE PropertyBuffer */ + cbBuffer, /* IN DWORD PropertyBufferSize */ + &cbRequired /* OUT PDWORD RequiredSize OPTIONAL */)) + { + winEr = GetLastError(); + if (winEr != ERROR_INSUFFICIENT_BUFFER) + { + NonStandardLogFlow(("VBoxNetCfgWinEnumNetDevices: SetupDiGetDeviceRegistryPropertyW (1) failed with %ld\n", winEr)); + break; + } + + pBuffer = (PBYTE)malloc(cbRequired); + if (!pBuffer) + { + NonStandardLogFlow(("VBoxNetCfgWinEnumNetDevices: Out of memory allocating %ld bytes\n", + cbRequired)); + winEr = ERROR_OUTOFMEMORY; + break; + } + + cbBuffer = cbRequired; + + if (!SetupDiGetDeviceRegistryPropertyW(hDevInfo,&Dev, + SPDRP_HARDWAREID, /* IN DWORD Property */ + NULL, /* OUT PDWORD PropertyRegDataType, OPTIONAL */ + pBuffer, /* OUT PBYTE PropertyBuffer */ + cbBuffer, /* IN DWORD PropertyBufferSize */ + &cbRequired /* OUT PDWORD RequiredSize OPTIONAL */)) + { + winEr = GetLastError(); + NonStandardLogFlow(("VBoxNetCfgWinEnumNetDevices: SetupDiGetDeviceRegistryPropertyW (2) failed with %ld\n", + winEr)); + break; + } + } + + PWSTR pCurId = (PWSTR)pBuffer; + size_t cCurId = wcslen(pCurId); + + NonStandardLogFlow(("VBoxNetCfgWinEnumNetDevices: Device %ld: %S\n", dwDevId, pCurId)); + + if (cCurId >= cPnPId) + { + NonStandardLogFlow(("!wcsnicmp(pCurId = (%S), pwszPnPId = (%S), cPnPId = (%d))\n", pCurId, pwszPnPId, cPnPId)); + + pCurId += cCurId - cPnPId; + if (!wcsnicmp(pCurId, pwszPnPId, cPnPId)) + { + if (!pfnCallback(hDevInfo, &Dev, pvContext)) + break; + } + } + } + + NonStandardLogFlow(("VBoxNetCfgWinEnumNetDevices: Found %ld devices total\n", dwDevId)); + + if (pBuffer) + free(pBuffer); + + hr = HRESULT_FROM_WIN32(winEr); + + SetupDiDestroyDeviceInfoList(hDevInfo); + } + else + { + DWORD winEr = GetLastError(); + NonStandardLogFlow(("VBoxNetCfgWinEnumNetDevices: SetupDiGetClassDevsExW failed with %ld\n", winEr)); + hr = HRESULT_FROM_WIN32(winEr); + } + + NonStandardLogFlow(("VBoxNetCfgWinEnumNetDevices: Ended with hr (0x%x)\n", hr)); + return hr; +} + +VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinRemoveAllNetDevicesOfId(IN LPCWSTR lpszPnPId) +{ + return VBoxNetCfgWinEnumNetDevices(lpszPnPId, vboxNetCfgWinRemoveAllNetDevicesOfIdCallback, NULL); +} + +VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinPropChangeAllNetDevicesOfId(IN LPCWSTR lpszPnPId, VBOXNECTFGWINPROPCHANGE_TYPE enmPcType) +{ + VBOXNECTFGWINPROPCHANGE Pc; + Pc.enmPcType = enmPcType; + Pc.hr = S_OK; + NonStandardLogFlow(("Calling VBoxNetCfgWinEnumNetDevices with lpszPnPId =(%S) and vboxNetCfgWinPropChangeAllNetDevicesOfIdCallback\n", lpszPnPId)); + + HRESULT hr = VBoxNetCfgWinEnumNetDevices(lpszPnPId, vboxNetCfgWinPropChangeAllNetDevicesOfIdCallback, &Pc); + if (!SUCCEEDED(hr)) + { + NonStandardLogFlow(("VBoxNetCfgWinEnumNetDevices failed 0x%x\n", hr)); + return hr; + } + + if (!SUCCEEDED(Pc.hr)) + { + NonStandardLogFlow(("vboxNetCfgWinPropChangeAllNetDevicesOfIdCallback failed 0x%x\n", Pc.hr)); + return Pc.hr; + } + + return S_OK; +} + +/* + * logging + */ +static VOID DoLogging(LPCSTR szString, ...) +{ + LOG_ROUTINE pfnRoutine = (LOG_ROUTINE)(*((void * volatile *)&g_Logger)); + if (pfnRoutine) + { + char szBuffer[4096] = {0}; + va_list va; + va_start(va, szString); + _vsnprintf(szBuffer, RT_ELEMENTS(szBuffer), szString, va); + va_end(va); + + pfnRoutine(szBuffer); + } +} + +VBOXNETCFGWIN_DECL(VOID) VBoxNetCfgWinSetLogging(IN LOG_ROUTINE pfnLog) +{ + *((void * volatile *)&g_Logger) = pfnLog; +} + +/* + * IP configuration API + */ +/* network settings config */ +/** + * Strong referencing operators. Used as a second argument to ComPtr<>/ComObjPtr<>. + */ +template <class C> +class ComStrongRef +{ +protected: + + static void addref (C *p) { p->AddRef(); } + static void release (C *p) { p->Release(); } +}; + + +/** + * Base template for smart COM pointers. Not intended to be used directly. + */ +template <class C, template <class> class RefOps = ComStrongRef> +class ComPtrBase : protected RefOps <C> +{ +public: + + /* special template to disable AddRef()/Release() */ + template <class I> + class NoAddRefRelease : public I + { + private: +#if !defined (VBOX_WITH_XPCOM) + STDMETHOD_(ULONG, AddRef)() = 0; + STDMETHOD_(ULONG, Release)() = 0; +#else /* !defined (VBOX_WITH_XPCOM) */ + NS_IMETHOD_(nsrefcnt) AddRef(void) = 0; + NS_IMETHOD_(nsrefcnt) Release(void) = 0; +#endif /* !defined (VBOX_WITH_XPCOM) */ + }; + +protected: + + ComPtrBase () : p (NULL) {} + ComPtrBase (const ComPtrBase &that) : p (that.p) { addref(); } + ComPtrBase (C *that_p) : p (that_p) { addref(); } + + ~ComPtrBase() { release(); } + + ComPtrBase &operator= (const ComPtrBase &that) + { + safe_assign (that.p); + return *this; + } + + ComPtrBase &operator= (C *that_p) + { + safe_assign (that_p); + return *this; + } + +public: + + void setNull() + { + release(); + p = NULL; + } + + bool isNull() const + { + return (p == NULL); + } + + bool operator! () const { return isNull(); } + + bool operator< (C* that_p) const { return p < that_p; } + bool operator== (C* that_p) const { return p == that_p; } + + template <class I> + bool equalsTo (I *aThat) const + { + return ComPtrEquals (p, aThat); + } + + template <class OC> + bool equalsTo (const ComPtrBase <OC> &oc) const + { + return equalsTo ((OC *) oc); + } + + /** Intended to pass instances as in parameters to interface methods */ + operator C* () const { return p; } + + /** + * Dereferences the instance (redirects the -> operator to the managed + * pointer). + */ + NoAddRefRelease <C> *operator-> () const + { + AssertMsg (p, ("Managed pointer must not be null\n")); + return (NoAddRefRelease <C> *) p; + } + + template <class I> + HRESULT queryInterfaceTo (I **pp) const + { + if (pp) + { + if (p) + { + return p->QueryInterface (COM_IIDOF (I), (void **) pp); + } + else + { + *pp = NULL; + return S_OK; + } + } + + return E_INVALIDARG; + } + + /** Intended to pass instances as out parameters to interface methods */ + C **asOutParam() + { + setNull(); + return &p; + } + +private: + + void addref() + { + if (p) + RefOps <C>::addref (p); + } + + void release() + { + if (p) + RefOps <C>::release (p); + } + + void safe_assign (C *that_p) + { + /* be aware of self-assignment */ + if (that_p) + RefOps <C>::addref (that_p); + release(); + p = that_p; + } + + C *p; +}; + +/** + * Smart COM pointer wrapper that automatically manages refcounting of + * interface pointers. + * + * @param I COM interface class + */ +template <class I, template <class> class RefOps = ComStrongRef> +class ComPtr : public ComPtrBase <I, RefOps> +{ + typedef ComPtrBase <I, RefOps> Base; + +public: + + ComPtr () : Base() {} + ComPtr (const ComPtr &that) : Base(that) {} + ComPtr &operator= (const ComPtr &that) + { + Base::operator= (that); + return *this; + } + + template <class OI> + ComPtr (OI *that_p) : Base () { operator= (that_p); } + + /* specialization for I */ + ComPtr (I *that_p) : Base (that_p) {} + + template <class OC> + ComPtr (const ComPtr <OC, RefOps> &oc) : Base () { operator= ((OC *) oc); } + + template <class OI> + ComPtr &operator= (OI *that_p) + { + if (that_p) + that_p->QueryInterface (COM_IIDOF (I), (void **) Base::asOutParam()); + else + Base::setNull(); + return *this; + } + + /* specialization for I */ + ComPtr &operator=(I *that_p) + { + Base::operator= (that_p); + return *this; + } + + template <class OC> + ComPtr &operator= (const ComPtr <OC, RefOps> &oc) + { + return operator= ((OC *) oc); + } +}; + +static HRESULT netIfWinFindAdapterClassById(IWbemServices * pSvc, const GUID * pGuid, IWbemClassObject **pAdapterConfig) +{ + HRESULT hr; + WCHAR wszQuery[256]; + WCHAR wszGuid[50]; + + int length = StringFromGUID2(*pGuid, wszGuid, RT_ELEMENTS(wszGuid)); + if (length) + { + swprintf(wszQuery, L"SELECT * FROM Win32_NetworkAdapterConfiguration WHERE SettingID = \"%s\"", wszGuid); + IEnumWbemClassObject* pEnumerator = NULL; + hr = pSvc->ExecQuery(bstr_t("WQL"), bstr_t(wszQuery), WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, + NULL, &pEnumerator); + if (SUCCEEDED(hr)) + { + if (pEnumerator) + { + IWbemClassObject *pclsObj; + ULONG uReturn = 0; + hr = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn); + NonStandardLogFlow(("netIfWinFindAdapterClassById: IEnumWbemClassObject::Next -> hr=0x%x pclsObj=%p uReturn=%u 42=%u\n", + hr, (void *)pclsObj, uReturn, 42)); + if (SUCCEEDED(hr)) + { + if (uReturn && pclsObj != NULL) + { + *pAdapterConfig = pclsObj; + pEnumerator->Release(); + NonStandardLogFlow(("netIfWinFindAdapterClassById: S_OK and %p\n", *pAdapterConfig)); + return S_OK; + } + + hr = E_FAIL; + } + + pEnumerator->Release(); + } + else + { + NonStandardLogFlow(("ExecQuery returned no enumerator\n")); + hr = E_FAIL; + } + } + else + NonStandardLogFlow(("ExecQuery failed (0x%x)\n", hr)); + } + else + { + DWORD winEr = GetLastError(); + hr = HRESULT_FROM_WIN32( winEr ); + if (SUCCEEDED(hr)) + hr = E_FAIL; + NonStandardLogFlow(("StringFromGUID2 failed winEr=%u, hr=0x%x\n", winEr, hr)); + } + + NonStandardLogFlow(("netIfWinFindAdapterClassById: 0x%x and %p\n", hr, *pAdapterConfig)); + return hr; +} + +static HRESULT netIfWinIsHostOnly(IWbemClassObject * pAdapterConfig, BOOL * pbIsHostOnly) +{ + VARIANT vtServiceName; + VariantInit(&vtServiceName); + + HRESULT hr = pAdapterConfig->Get(L"ServiceName", 0 /*lFlags*/, &vtServiceName, NULL /*pvtType*/, NULL /*plFlavor*/); + if (SUCCEEDED(hr)) + { + *pbIsHostOnly = bstr_t(vtServiceName.bstrVal) == bstr_t("VBoxNetAdp"); + + VariantClear(&vtServiceName); + } + + return hr; +} + +static HRESULT netIfWinGetIpSettings(IWbemClassObject * pAdapterConfig, ULONG *pIpv4, ULONG *pMaskv4) +{ + VARIANT vtIp; + HRESULT hr; + VariantInit(&vtIp); + + *pIpv4 = 0; + *pMaskv4 = 0; + + hr = pAdapterConfig->Get(L"IPAddress", 0, &vtIp, 0, 0); + if (SUCCEEDED(hr)) + { + if (vtIp.vt == (VT_ARRAY | VT_BSTR)) + { + VARIANT vtMask; + VariantInit(&vtMask); + hr = pAdapterConfig->Get(L"IPSubnet", 0, &vtMask, 0, 0); + if (SUCCEEDED(hr)) + { + if (vtMask.vt == (VT_ARRAY | VT_BSTR)) + { + SAFEARRAY * pIpArray = vtIp.parray; + SAFEARRAY * pMaskArray = vtMask.parray; + if (pIpArray && pMaskArray) + { + BSTR pCurIp; + BSTR pCurMask; + for (LONG i = 0; + SafeArrayGetElement(pIpArray, &i, (PVOID)&pCurIp) == S_OK + && SafeArrayGetElement(pMaskArray, &i, (PVOID)&pCurMask) == S_OK; + i++) + { + bstr_t ip(pCurIp); + + ULONG Ipv4 = inet_addr((char*)(ip)); + if (Ipv4 != INADDR_NONE) + { + *pIpv4 = Ipv4; + bstr_t mask(pCurMask); + *pMaskv4 = inet_addr((char*)(mask)); + break; + } + } + } + } + else + { + *pIpv4 = 0; + *pMaskv4 = 0; + } + + VariantClear(&vtMask); + } + } + else + { + *pIpv4 = 0; + *pMaskv4 = 0; + } + + VariantClear(&vtIp); + } + + return hr; +} + +#if 0 /* unused */ + +static HRESULT netIfWinHasIpSettings(IWbemClassObject * pAdapterConfig, SAFEARRAY * pCheckIp, SAFEARRAY * pCheckMask, bool *pFound) +{ + VARIANT vtIp; + HRESULT hr; + VariantInit(&vtIp); + + *pFound = false; + + hr = pAdapterConfig->Get(L"IPAddress", 0, &vtIp, 0, 0); + if (SUCCEEDED(hr)) + { + VARIANT vtMask; + VariantInit(&vtMask); + hr = pAdapterConfig->Get(L"IPSubnet", 0, &vtMask, 0, 0); + if (SUCCEEDED(hr)) + { + SAFEARRAY * pIpArray = vtIp.parray; + SAFEARRAY * pMaskArray = vtMask.parray; + if (pIpArray && pMaskArray) + { + BSTR pIp, pMask; + for (LONG k = 0; + SafeArrayGetElement(pCheckIp, &k, (PVOID)&pIp) == S_OK + && SafeArrayGetElement(pCheckMask, &k, (PVOID)&pMask) == S_OK; + k++) + { + BSTR pCurIp; + BSTR pCurMask; + for (LONG i = 0; + SafeArrayGetElement(pIpArray, &i, (PVOID)&pCurIp) == S_OK + && SafeArrayGetElement(pMaskArray, &i, (PVOID)&pCurMask) == S_OK; + i++) + { + if (!wcsicmp(pCurIp, pIp)) + { + if (!wcsicmp(pCurMask, pMask)) + *pFound = true; + break; + } + } + } + } + + + VariantClear(&vtMask); + } + + VariantClear(&vtIp); + } + + return hr; +} + +static HRESULT netIfWinWaitIpSettings(IWbemServices *pSvc, const GUID * pGuid, SAFEARRAY * pCheckIp, SAFEARRAY * pCheckMask, ULONG sec2Wait, bool *pFound) +{ + /* on Vista we need to wait for the address to get applied */ + /* wait for the address to appear in the list */ + HRESULT hr = S_OK; + ULONG i; + *pFound = false; + ComPtr <IWbemClassObject> pAdapterConfig; + for (i = 0; + (hr = netIfWinFindAdapterClassById(pSvc, pGuid, pAdapterConfig.asOutParam())) == S_OK + && (hr = netIfWinHasIpSettings(pAdapterConfig, pCheckIp, pCheckMask, pFound)) == S_OK + && !(*pFound) + && i < sec2Wait/6; + i++) + { + Sleep(6000); + } + + return hr; +} + +#endif /* unused */ + +static HRESULT netIfWinCreateIWbemServices(IWbemServices ** ppSvc) +{ + IWbemLocator *pLoc = NULL; + HRESULT hr = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID *) &pLoc); + if (SUCCEEDED(hr)) + { + IWbemServices *pSvc = NULL; + hr = pLoc->ConnectServer(bstr_t(L"ROOT\\CIMV2"), /* [in] const BSTR strNetworkResource */ + NULL, /* [in] const BSTR strUser */ + NULL, /* [in] const BSTR strPassword */ + 0, /* [in] const BSTR strLocale */ + NULL, /* [in] LONG lSecurityFlags */ + 0, /* [in] const BSTR strAuthority */ + 0, /* [in] IWbemContext* pCtx */ + &pSvc /* [out] IWbemServices** ppNamespace */); + if (SUCCEEDED(hr)) + { + hr = CoSetProxyBlanket(pSvc, /* IUnknown * pProxy */ + RPC_C_AUTHN_WINNT, /* DWORD dwAuthnSvc */ + RPC_C_AUTHZ_NONE, /* DWORD dwAuthzSvc */ + NULL, /* WCHAR * pServerPrincName */ + RPC_C_AUTHN_LEVEL_CALL, /* DWORD dwAuthnLevel */ + RPC_C_IMP_LEVEL_IMPERSONATE, /* DWORD dwImpLevel */ + NULL, /* RPC_AUTH_IDENTITY_HANDLE pAuthInfo */ + EOAC_NONE /* DWORD dwCapabilities */ + ); + if (SUCCEEDED(hr)) + { + *ppSvc = pSvc; + /* do not need it any more */ + pLoc->Release(); + return hr; + } + else + NonStandardLogFlow(("CoSetProxyBlanket failed, hr (0x%x)\n", hr)); + + pSvc->Release(); + } + else + NonStandardLogFlow(("ConnectServer failed, hr (0x%x)\n", hr)); + pLoc->Release(); + } + else + NonStandardLogFlow(("CoCreateInstance failed, hr (0x%x)\n", hr)); + return hr; +} + +static HRESULT netIfWinAdapterConfigPath(IWbemClassObject *pObj, BSTR * pStr) +{ + VARIANT index; + HRESULT hr = pObj->Get(L"Index", 0, &index, 0, 0); + if (SUCCEEDED(hr)) + { + WCHAR strIndex[8]; + swprintf(strIndex, L"%u", index.uintVal); + *pStr = (bstr_t(L"Win32_NetworkAdapterConfiguration.Index='") + strIndex + "'").copy(); + } + else + NonStandardLogFlow(("Get failed, hr (0x%x)\n", hr)); + return hr; +} + +static HRESULT netIfExecMethod(IWbemServices * pSvc, IWbemClassObject *pClass, BSTR ObjPath, + BSTR MethodName, LPWSTR *pArgNames, LPVARIANT *pArgs, UINT cArgs, + IWbemClassObject** ppOutParams + ) +{ + HRESULT hr = S_OK; + ComPtr<IWbemClassObject> pInParamsDefinition; + ComPtr<IWbemClassObject> pClassInstance; + + if (cArgs) + { + hr = pClass->GetMethod(MethodName, 0, pInParamsDefinition.asOutParam(), NULL); + if (SUCCEEDED(hr)) + { + hr = pInParamsDefinition->SpawnInstance(0, pClassInstance.asOutParam()); + if (SUCCEEDED(hr)) + { + for (UINT i = 0; i < cArgs; i++) + { + hr = pClassInstance->Put(pArgNames[i], 0, + pArgs[i], 0); + if (FAILED(hr)) + break; + } + } + } + } + + if (SUCCEEDED(hr)) + { + IWbemClassObject* pOutParams = NULL; + hr = pSvc->ExecMethod(ObjPath, MethodName, 0, NULL, pClassInstance, &pOutParams, NULL); + if (SUCCEEDED(hr)) + { + *ppOutParams = pOutParams; + } + } + + return hr; +} + +static HRESULT netIfWinCreateIpArray(SAFEARRAY **ppArray, in_addr* aIp, UINT cIp) +{ + HRESULT hr = S_OK; /* MSC maybe used uninitialized */ + SAFEARRAY * pIpArray = SafeArrayCreateVector(VT_BSTR, 0, cIp); + if (pIpArray) + { + for (UINT i = 0; i < cIp; i++) + { + char* addr = inet_ntoa(aIp[i]); + BSTR val = bstr_t(addr).copy(); + long aIndex[1]; + aIndex[0] = i; + hr = SafeArrayPutElement(pIpArray, aIndex, val); + if (FAILED(hr)) + { + SysFreeString(val); + SafeArrayDestroy(pIpArray); + break; + } + } + + if (SUCCEEDED(hr)) + { + *ppArray = pIpArray; + } + } + else + hr = HRESULT_FROM_WIN32(GetLastError()); + + return hr; +} + +#if 0 /* unused */ +static HRESULT netIfWinCreateIpArrayV4V6(SAFEARRAY **ppArray, BSTR Ip) +{ + HRESULT hr; + SAFEARRAY *pIpArray = SafeArrayCreateVector(VT_BSTR, 0, 1); + if (pIpArray) + { + BSTR val = bstr_t(Ip, false).copy(); + long aIndex[1]; + aIndex[0] = 0; + hr = SafeArrayPutElement(pIpArray, aIndex, val); + if (FAILED(hr)) + { + SysFreeString(val); + SafeArrayDestroy(pIpArray); + } + + if (SUCCEEDED(hr)) + { + *ppArray = pIpArray; + } + } + else + hr = HRESULT_FROM_WIN32(GetLastError()); + + return hr; +} +#endif + + +static HRESULT netIfWinCreateIpArrayVariantV4(VARIANT * pIpAddresses, in_addr* aIp, UINT cIp) +{ + HRESULT hr; + VariantInit(pIpAddresses); + pIpAddresses->vt = VT_ARRAY | VT_BSTR; + SAFEARRAY *pIpArray; + hr = netIfWinCreateIpArray(&pIpArray, aIp, cIp); + if (SUCCEEDED(hr)) + { + pIpAddresses->parray = pIpArray; + } + return hr; +} + +#if 0 /* unused */ +static HRESULT netIfWinCreateIpArrayVariantV4V6(VARIANT * pIpAddresses, BSTR Ip) +{ + HRESULT hr; + VariantInit(pIpAddresses); + pIpAddresses->vt = VT_ARRAY | VT_BSTR; + SAFEARRAY *pIpArray; + hr = netIfWinCreateIpArrayV4V6(&pIpArray, Ip); + if (SUCCEEDED(hr)) + { + pIpAddresses->parray = pIpArray; + } + return hr; +} +#endif + +static HRESULT netIfWinEnableStatic(IWbemServices *pSvc, const GUID *pGuid, BSTR ObjPath, VARIANT *pIp, VARIANT *pMask) +{ + ComPtr<IWbemClassObject> pClass; + BSTR ClassName = SysAllocString(L"Win32_NetworkAdapterConfiguration"); + HRESULT hr; + if (ClassName) + { + hr = pSvc->GetObject(ClassName, 0, NULL, pClass.asOutParam(), NULL); + if (SUCCEEDED(hr)) + { + LPWSTR argNames[] = {L"IPAddress", L"SubnetMask"}; + LPVARIANT args[] = {pIp, pMask}; + ComPtr<IWbemClassObject> pOutParams; + + hr = netIfExecMethod(pSvc, pClass, ObjPath, bstr_t(L"EnableStatic"), argNames, args, 2, pOutParams.asOutParam()); + if (SUCCEEDED(hr)) + { + VARIANT varReturnValue; + hr = pOutParams->Get(bstr_t(L"ReturnValue"), 0, + &varReturnValue, NULL, 0); + Assert(SUCCEEDED(hr)); + if (SUCCEEDED(hr)) + { +// Assert(varReturnValue.vt == VT_UINT); + int winEr = varReturnValue.uintVal; + switch (winEr) + { + case 0: + { + hr = S_OK; +// bool bFound; +// HRESULT tmpHr = netIfWinWaitIpSettings(pSvc, pGuid, pIp->parray, pMask->parray, 180, &bFound); + NOREF(pGuid); + break; + } + default: + hr = HRESULT_FROM_WIN32( winEr ); + break; + } + } + } + } + SysFreeString(ClassName); + } + else + hr = HRESULT_FROM_WIN32(GetLastError()); + + return hr; +} + + +static HRESULT netIfWinEnableStaticV4(IWbemServices * pSvc, const GUID * pGuid, BSTR ObjPath, in_addr* aIp, in_addr * aMask, UINT cIp) +{ + VARIANT ipAddresses; + HRESULT hr = netIfWinCreateIpArrayVariantV4(&ipAddresses, aIp, cIp); + if (SUCCEEDED(hr)) + { + VARIANT ipMasks; + hr = netIfWinCreateIpArrayVariantV4(&ipMasks, aMask, cIp); + if (SUCCEEDED(hr)) + { + hr = netIfWinEnableStatic(pSvc, pGuid, ObjPath, &ipAddresses, &ipMasks); + VariantClear(&ipMasks); + } + VariantClear(&ipAddresses); + } + return hr; +} + +#if 0 /* unused */ + +static HRESULT netIfWinEnableStaticV4V6(IWbemServices * pSvc, const GUID * pGuid, BSTR ObjPath, BSTR Ip, BSTR Mask) +{ + VARIANT ipAddresses; + HRESULT hr = netIfWinCreateIpArrayVariantV4V6(&ipAddresses, Ip); + if (SUCCEEDED(hr)) + { + VARIANT ipMasks; + hr = netIfWinCreateIpArrayVariantV4V6(&ipMasks, Mask); + if (SUCCEEDED(hr)) + { + hr = netIfWinEnableStatic(pSvc, pGuid, ObjPath, &ipAddresses, &ipMasks); + VariantClear(&ipMasks); + } + VariantClear(&ipAddresses); + } + return hr; +} + +/* win API allows to set gw metrics as well, we are not setting them */ +static HRESULT netIfWinSetGateways(IWbemServices * pSvc, BSTR ObjPath, VARIANT * pGw) +{ + ComPtr<IWbemClassObject> pClass; + BSTR ClassName = SysAllocString(L"Win32_NetworkAdapterConfiguration"); + HRESULT hr; + if (ClassName) + { + hr = pSvc->GetObject(ClassName, 0, NULL, pClass.asOutParam(), NULL); + if (SUCCEEDED(hr)) + { + LPWSTR argNames[] = {L"DefaultIPGateway"}; + LPVARIANT args[] = {pGw}; + ComPtr<IWbemClassObject> pOutParams; + + hr = netIfExecMethod(pSvc, pClass, ObjPath, bstr_t(L"SetGateways"), argNames, args, 1, pOutParams.asOutParam()); + if (SUCCEEDED(hr)) + { + VARIANT varReturnValue; + hr = pOutParams->Get(bstr_t(L"ReturnValue"), 0, &varReturnValue, NULL, 0); + Assert(SUCCEEDED(hr)); + if (SUCCEEDED(hr)) + { +// Assert(varReturnValue.vt == VT_UINT); + int winEr = varReturnValue.uintVal; + switch (winEr) + { + case 0: + hr = S_OK; + break; + default: + hr = HRESULT_FROM_WIN32( winEr ); + break; + } + } + } + } + SysFreeString(ClassName); + } + else + hr = HRESULT_FROM_WIN32(GetLastError()); + + return hr; +} + +/* win API allows to set gw metrics as well, we are not setting them */ +static HRESULT netIfWinSetGatewaysV4(IWbemServices * pSvc, BSTR ObjPath, in_addr* aGw, UINT cGw) +{ + VARIANT gwais; + HRESULT hr = netIfWinCreateIpArrayVariantV4(&gwais, aGw, cGw); + if (SUCCEEDED(hr)) + { + netIfWinSetGateways(pSvc, ObjPath, &gwais); + VariantClear(&gwais); + } + return hr; +} + +/* win API allows to set gw metrics as well, we are not setting them */ +static HRESULT netIfWinSetGatewaysV4V6(IWbemServices * pSvc, BSTR ObjPath, BSTR Gw) +{ + VARIANT vGw; + HRESULT hr = netIfWinCreateIpArrayVariantV4V6(&vGw, Gw); + if (SUCCEEDED(hr)) + { + netIfWinSetGateways(pSvc, ObjPath, &vGw); + VariantClear(&vGw); + } + return hr; +} + +#endif /* unused */ + +static HRESULT netIfWinEnableDHCP(IWbemServices * pSvc, BSTR ObjPath) +{ + ComPtr<IWbemClassObject> pClass; + BSTR ClassName = SysAllocString(L"Win32_NetworkAdapterConfiguration"); + HRESULT hr; + if (ClassName) + { + hr = pSvc->GetObject(ClassName, 0, NULL, pClass.asOutParam(), NULL); + if (SUCCEEDED(hr)) + { + ComPtr<IWbemClassObject> pOutParams; + + hr = netIfExecMethod(pSvc, pClass, ObjPath, bstr_t(L"EnableDHCP"), NULL, NULL, 0, pOutParams.asOutParam()); + if (SUCCEEDED(hr)) + { + VARIANT varReturnValue; + hr = pOutParams->Get(bstr_t(L"ReturnValue"), 0, + &varReturnValue, NULL, 0); + Assert(SUCCEEDED(hr)); + if (SUCCEEDED(hr)) + { +// Assert(varReturnValue.vt == VT_UINT); + int winEr = varReturnValue.uintVal; + switch (winEr) + { + case 0: + hr = S_OK; + break; + default: + hr = HRESULT_FROM_WIN32( winEr ); + break; + } + } + } + } + SysFreeString(ClassName); + } + else + hr = HRESULT_FROM_WIN32(GetLastError()); + + return hr; +} + +static HRESULT netIfWinDhcpRediscover(IWbemServices * pSvc, BSTR ObjPath) +{ + ComPtr<IWbemClassObject> pClass; + BSTR ClassName = SysAllocString(L"Win32_NetworkAdapterConfiguration"); + HRESULT hr; + if (ClassName) + { + hr = pSvc->GetObject(ClassName, 0, NULL, pClass.asOutParam(), NULL); + if (SUCCEEDED(hr)) + { + ComPtr<IWbemClassObject> pOutParams; + + hr = netIfExecMethod(pSvc, pClass, ObjPath, bstr_t(L"ReleaseDHCPLease"), NULL, NULL, 0, pOutParams.asOutParam()); + if (SUCCEEDED(hr)) + { + VARIANT varReturnValue; + hr = pOutParams->Get(bstr_t(L"ReturnValue"), 0, &varReturnValue, NULL, 0); + Assert(SUCCEEDED(hr)); + if (SUCCEEDED(hr)) + { +// Assert(varReturnValue.vt == VT_UINT); + int winEr = varReturnValue.uintVal; + if (winEr == 0) + { + hr = netIfExecMethod(pSvc, pClass, ObjPath, bstr_t(L"RenewDHCPLease"), NULL, NULL, 0, pOutParams.asOutParam()); + if (SUCCEEDED(hr)) + { + VARIANT varReturnValue; + hr = pOutParams->Get(bstr_t(L"ReturnValue"), 0, &varReturnValue, NULL, 0); + Assert(SUCCEEDED(hr)); + if (SUCCEEDED(hr)) + { + // Assert(varReturnValue.vt == VT_UINT); + int winEr = varReturnValue.uintVal; + if (winEr == 0) + hr = S_OK; + else + hr = HRESULT_FROM_WIN32( winEr ); + } + } + } + else + hr = HRESULT_FROM_WIN32( winEr ); + } + } + } + SysFreeString(ClassName); + } + else + hr = HRESULT_FROM_WIN32(GetLastError()); + + return hr; +} + +static HRESULT vboxNetCfgWinIsDhcpEnabled(IWbemClassObject * pAdapterConfig, BOOL *pEnabled) +{ + VARIANT vtEnabled; + HRESULT hr = pAdapterConfig->Get(L"DHCPEnabled", 0, &vtEnabled, 0, 0); + if (SUCCEEDED(hr)) + *pEnabled = vtEnabled.boolVal; + return hr; +} + +VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinGetAdapterSettings(IN const GUID * pGuid, OUT PADAPTER_SETTINGS pSettings) +{ + HRESULT hr; + ComPtr <IWbemServices> pSvc; + hr = netIfWinCreateIWbemServices(pSvc.asOutParam()); + if (SUCCEEDED(hr)) + { + ComPtr<IWbemClassObject> pAdapterConfig; + hr = netIfWinFindAdapterClassById(pSvc, pGuid, pAdapterConfig.asOutParam()); + if (SUCCEEDED(hr)) + { + hr = vboxNetCfgWinIsDhcpEnabled(pAdapterConfig, &pSettings->bDhcp); + if (SUCCEEDED(hr)) + hr = netIfWinGetIpSettings(pAdapterConfig, &pSettings->ip, &pSettings->mask); + } + } + + return hr; +} + +VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinIsDhcpEnabled(const GUID * pGuid, BOOL *pEnabled) +{ + HRESULT hr; + ComPtr <IWbemServices> pSvc; + hr = netIfWinCreateIWbemServices(pSvc.asOutParam()); + if (SUCCEEDED(hr)) + { + ComPtr<IWbemClassObject> pAdapterConfig; + hr = netIfWinFindAdapterClassById(pSvc, pGuid, pAdapterConfig.asOutParam()); + if (SUCCEEDED(hr)) + { + VARIANT vtEnabled; + hr = pAdapterConfig->Get(L"DHCPEnabled", 0, &vtEnabled, 0, 0); + if (SUCCEEDED(hr)) + *pEnabled = vtEnabled.boolVal; + } + } + + return hr; +} + +VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinEnableStaticIpConfig(IN const GUID *pGuid, IN ULONG ip, IN ULONG mask) +{ + NonStandardLogFlow(("VBoxNetCfgWinEnableStaticIpConfig: ip=0x%x mask=0x%x\n", ip, mask)); + ComPtr<IWbemServices> pSvc; + HRESULT hr = netIfWinCreateIWbemServices(pSvc.asOutParam()); + if (SUCCEEDED(hr)) + { + ComPtr<IWbemClassObject> pAdapterConfig; + hr = netIfWinFindAdapterClassById(pSvc, pGuid, pAdapterConfig.asOutParam()); + if (SUCCEEDED(hr)) + { + BOOL bIsHostOnly; + hr = netIfWinIsHostOnly(pAdapterConfig, &bIsHostOnly); + if (SUCCEEDED(hr)) + { + if (bIsHostOnly) + { + in_addr aIp[1]; + in_addr aMask[1]; + aIp[0].S_un.S_addr = ip; + aMask[0].S_un.S_addr = mask; + + BSTR ObjPath; + hr = netIfWinAdapterConfigPath(pAdapterConfig, &ObjPath); + if (SUCCEEDED(hr)) + { + hr = netIfWinEnableStaticV4(pSvc, pGuid, ObjPath, aIp, aMask, ip != 0 ? 1 : 0); + if (SUCCEEDED(hr)) + { +#if 0 + in_addr aGw[1]; + aGw[0].S_un.S_addr = gw; + hr = netIfWinSetGatewaysV4(pSvc, ObjPath, aGw, 1); + if (SUCCEEDED(hr)) +#endif + { + } + } + SysFreeString(ObjPath); + } + } + else + { + hr = E_FAIL; + } + } + } + } + + NonStandardLogFlow(("VBoxNetCfgWinEnableStaticIpConfig: returns 0x%x\n", hr)); + return hr; +} + +#if 0 +static HRESULT netIfEnableStaticIpConfigV6(const GUID *pGuid, IN_BSTR aIPV6Address, IN_BSTR aIPV6Mask, IN_BSTR aIPV6DefaultGateway) +{ + HRESULT hr; + ComPtr <IWbemServices> pSvc; + hr = netIfWinCreateIWbemServices(pSvc.asOutParam()); + if (SUCCEEDED(hr)) + { + ComPtr<IWbemClassObject> pAdapterConfig; + hr = netIfWinFindAdapterClassById(pSvc, pGuid, pAdapterConfig.asOutParam()); + if (SUCCEEDED(hr)) + { + BSTR ObjPath; + hr = netIfWinAdapterConfigPath(pAdapterConfig, &ObjPath); + if (SUCCEEDED(hr)) + { + hr = netIfWinEnableStaticV4V6(pSvc, pAdapterConfig, ObjPath, aIPV6Address, aIPV6Mask); + if (SUCCEEDED(hr)) + { + if (aIPV6DefaultGateway) + { + hr = netIfWinSetGatewaysV4V6(pSvc, ObjPath, aIPV6DefaultGateway); + } + if (SUCCEEDED(hr)) + { +// hr = netIfWinUpdateConfig(pIf); + } + } + SysFreeString(ObjPath); + } + } + } + + return SUCCEEDED(hr) ? VINF_SUCCESS : VERR_GENERAL_FAILURE; +} + +static HRESULT netIfEnableStaticIpConfigV6(const GUID *pGuid, IN_BSTR aIPV6Address, ULONG aIPV6MaskPrefixLength) +{ + RTNETADDRIPV6 Mask; + int rc = RTNetPrefixToMaskIPv6(aIPV6MaskPrefixLength, &Mask); + if (RT_SUCCESS(rc)) + { + Bstr maskStr = composeIPv6Address(&Mask); + rc = netIfEnableStaticIpConfigV6(pGuid, aIPV6Address, maskStr, NULL); + } + return rc; +} +#endif + +VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinEnableDynamicIpConfig(IN const GUID *pGuid) +{ + HRESULT hr; + ComPtr <IWbemServices> pSvc; + hr = netIfWinCreateIWbemServices(pSvc.asOutParam()); + if (SUCCEEDED(hr)) + { + ComPtr<IWbemClassObject> pAdapterConfig; + hr = netIfWinFindAdapterClassById(pSvc, pGuid, pAdapterConfig.asOutParam()); + if (SUCCEEDED(hr)) + { + BOOL bIsHostOnly; + hr = netIfWinIsHostOnly(pAdapterConfig, &bIsHostOnly); + if (SUCCEEDED(hr)) + { + if (bIsHostOnly) + { + BSTR ObjPath; + hr = netIfWinAdapterConfigPath(pAdapterConfig, &ObjPath); + if (SUCCEEDED(hr)) + { + hr = netIfWinEnableDHCP(pSvc, ObjPath); + if (SUCCEEDED(hr)) + { +// hr = netIfWinUpdateConfig(pIf); + } + SysFreeString(ObjPath); + } + } + else + { + hr = E_FAIL; + } + } + } + } + + + return hr; +} + +VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinDhcpRediscover(IN const GUID *pGuid) +{ + HRESULT hr; + ComPtr <IWbemServices> pSvc; + hr = netIfWinCreateIWbemServices(pSvc.asOutParam()); + if (SUCCEEDED(hr)) + { + ComPtr<IWbemClassObject> pAdapterConfig; + hr = netIfWinFindAdapterClassById(pSvc, pGuid, pAdapterConfig.asOutParam()); + if (SUCCEEDED(hr)) + { + BOOL bIsHostOnly; + hr = netIfWinIsHostOnly(pAdapterConfig, &bIsHostOnly); + if (SUCCEEDED(hr)) + { + if (bIsHostOnly) + { + BSTR ObjPath; + hr = netIfWinAdapterConfigPath(pAdapterConfig, &ObjPath); + if (SUCCEEDED(hr)) + { + hr = netIfWinDhcpRediscover(pSvc, ObjPath); + if (SUCCEEDED(hr)) + { + //hr = netIfWinUpdateConfig(pIf); + } + SysFreeString(ObjPath); + } + } + else + { + hr = E_FAIL; + } + } + } + } + + + return hr; +} + +static const char *vboxNetCfgWinAddrToStr(char *pszBuf, LPSOCKADDR pAddr) +{ + switch (pAddr->sa_family) + { + case AF_INET: + sprintf(pszBuf, "%d.%d.%d.%d", + ((PSOCKADDR_IN)pAddr)->sin_addr.S_un.S_un_b.s_b1, + ((PSOCKADDR_IN)pAddr)->sin_addr.S_un.S_un_b.s_b2, + ((PSOCKADDR_IN)pAddr)->sin_addr.S_un.S_un_b.s_b3, + ((PSOCKADDR_IN)pAddr)->sin_addr.S_un.S_un_b.s_b4); + break; + case AF_INET6: + sprintf(pszBuf, "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x", + ((PSOCKADDR_IN6)pAddr)->sin6_addr.s6_addr[0], ((PSOCKADDR_IN6)pAddr)->sin6_addr.s6_addr[1], + ((PSOCKADDR_IN6)pAddr)->sin6_addr.s6_addr[2], ((PSOCKADDR_IN6)pAddr)->sin6_addr.s6_addr[3], + ((PSOCKADDR_IN6)pAddr)->sin6_addr.s6_addr[4], ((PSOCKADDR_IN6)pAddr)->sin6_addr.s6_addr[5], + ((PSOCKADDR_IN6)pAddr)->sin6_addr.s6_addr[6], ((PSOCKADDR_IN6)pAddr)->sin6_addr.s6_addr[7], + ((PSOCKADDR_IN6)pAddr)->sin6_addr.s6_addr[8], ((PSOCKADDR_IN6)pAddr)->sin6_addr.s6_addr[9], + ((PSOCKADDR_IN6)pAddr)->sin6_addr.s6_addr[10], ((PSOCKADDR_IN6)pAddr)->sin6_addr.s6_addr[11], + ((PSOCKADDR_IN6)pAddr)->sin6_addr.s6_addr[12], ((PSOCKADDR_IN6)pAddr)->sin6_addr.s6_addr[13], + ((PSOCKADDR_IN6)pAddr)->sin6_addr.s6_addr[14], ((PSOCKADDR_IN6)pAddr)->sin6_addr.s6_addr[15]); + break; + default: + strcpy(pszBuf, "unknown"); + break; + } + return pszBuf; +} + +typedef bool (*PFNVBOXNETCFG_IPSETTINGS_CALLBACK) (ULONG ip, ULONG mask, PVOID pContext); + +static void vboxNetCfgWinEnumIpConfig(PIP_ADAPTER_ADDRESSES pAddresses, PFNVBOXNETCFG_IPSETTINGS_CALLBACK pfnCallback, PVOID pContext) +{ + PIP_ADAPTER_ADDRESSES pAdapter; + for (pAdapter = pAddresses; pAdapter; pAdapter = pAdapter->Next) + { + char szBuf[80]; + + NonStandardLogFlow(("+- Enumerating adapter '%ls' %s\n", pAdapter->FriendlyName, pAdapter->AdapterName)); + for (PIP_ADAPTER_PREFIX pPrefix = pAdapter->FirstPrefix; pPrefix; pPrefix = pPrefix->Next) + { + const char *pcszAddress = vboxNetCfgWinAddrToStr(szBuf, pPrefix->Address.lpSockaddr); + /* We are concerned with IPv4 only, ignore the rest. */ + if (pPrefix->Address.lpSockaddr->sa_family != AF_INET) + { + NonStandardLogFlow(("| +- %s %d: not IPv4, ignoring\n", pcszAddress, pPrefix->PrefixLength)); + continue; + } + /* Ignore invalid prefixes as well as host addresses. */ + if (pPrefix->PrefixLength < 1 || pPrefix->PrefixLength > 31) + { + NonStandardLogFlow(("| +- %s %d: host or broadcast, ignoring\n", pcszAddress, pPrefix->PrefixLength)); + continue; + } + /* Ignore multicast and beyond. */ + ULONG ip = ((struct sockaddr_in *)pPrefix->Address.lpSockaddr)->sin_addr.s_addr; + if ((ip & 0xF0) > 224) + { + NonStandardLogFlow(("| +- %s %d: multicast, ignoring\n", pcszAddress, pPrefix->PrefixLength)); + continue; + } + ULONG mask = htonl((~(((ULONG)~0) >> pPrefix->PrefixLength))); + bool fContinue = pfnCallback(ip, mask, pContext); + if (!fContinue) + { + NonStandardLogFlow(("| +- %s %d: CONFLICT!\n", pcszAddress, pPrefix->PrefixLength)); + return; + } + else + NonStandardLogFlow(("| +- %s %d: no conflict, moving on\n", pcszAddress, pPrefix->PrefixLength)); + } + } +} + +typedef struct _IPPROBE_CONTEXT +{ + ULONG Prefix; + bool bConflict; +}IPPROBE_CONTEXT, *PIPPROBE_CONTEXT; + +#define IPPROBE_INIT(_pContext, _addr) \ + ((_pContext)->bConflict = false, \ + (_pContext)->Prefix = _addr) + +#define IPPROBE_INIT_STR(_pContext, _straddr) \ + IPROBE_INIT(_pContext, inet_addr(_straddr)) + +static bool vboxNetCfgWinIpProbeCallback (ULONG ip, ULONG mask, PVOID pContext) +{ + PIPPROBE_CONTEXT pProbe = (PIPPROBE_CONTEXT)pContext; + + if ((ip & mask) == (pProbe->Prefix & mask)) + { + pProbe->bConflict = true; + return false; + } + + return true; +} + +VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinGenHostOnlyNetworkNetworkIp(OUT PULONG pNetIp, OUT PULONG pNetMask) +{ + DWORD dwRc; + HRESULT hr = S_OK; + /* + * MSDN recommends to pre-allocate a 15KB buffer. + */ + ULONG uBufLen = 15 * 1024; + PIP_ADAPTER_ADDRESSES pAddresses = (PIP_ADAPTER_ADDRESSES)malloc(uBufLen); + if (!pAddresses) + return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY); + dwRc = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, NULL, pAddresses, &uBufLen); + if (dwRc == ERROR_BUFFER_OVERFLOW) + { + /* Impressive! More than 10 adapters! Get more memory and try again. */ + free(pAddresses); + pAddresses = (PIP_ADAPTER_ADDRESSES)malloc(uBufLen); + if (!pAddresses) + return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY); + dwRc = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, NULL, pAddresses, &uBufLen); + } + if (dwRc == NO_ERROR) + { + IPPROBE_CONTEXT Context; + const ULONG ip192168 = inet_addr("192.168.0.0"); + srand(GetTickCount()); + + *pNetIp = 0; + *pNetMask = 0; + + for (int i = 0; i < 255; i++) + { + ULONG ipProbe = rand()*255/RAND_MAX; + ipProbe = ip192168 | (ipProbe << 16); + unsigned char *a = (unsigned char *)&ipProbe; + NonStandardLogFlow(("probing %d.%d.%d.%d\n", a[0], a[1], a[2], a[3])); + IPPROBE_INIT(&Context, ipProbe); + vboxNetCfgWinEnumIpConfig(pAddresses, vboxNetCfgWinIpProbeCallback, &Context); + if (!Context.bConflict) + { + NonStandardLogFlow(("found unused net %d.%d.%d.%d\n", a[0], a[1], a[2], a[3])); + *pNetIp = ipProbe; + *pNetMask = inet_addr("255.255.255.0"); + break; + } + } + if (*pNetIp == 0) + dwRc = ERROR_DHCP_ADDRESS_CONFLICT; + } + else + NonStandardLogFlow(("GetAdaptersAddresses err (%d)\n", dwRc)); + + if (pAddresses) + free(pAddresses); + + if (dwRc != NO_ERROR) + { + hr = HRESULT_FROM_WIN32(dwRc); + } + + return hr; +} + +/* + * convenience functions to perform netflt/adp manipulations + */ +#define VBOXNETCFGWIN_NETFLT_ID L"sun_VBoxNetFlt" +#define VBOXNETCFGWIN_NETFLT_MP_ID L"sun_VBoxNetFltmp" + +static HRESULT vboxNetCfgWinNetFltUninstall(IN INetCfg *pNc, DWORD InfRmFlags) +{ + INetCfgComponent *pNcc = NULL; + HRESULT hr = pNc->FindComponent(VBOXNETCFGWIN_NETFLT_ID, &pNcc); + if (hr == S_OK) + { + NonStandardLog("NetFlt is installed currently, uninstalling ...\n"); + + hr = VBoxNetCfgWinUninstallComponent(pNc, pNcc); + NonStandardLogFlow(("NetFlt component uninstallation ended with hr (0x%x)\n", hr)); + + pNcc->Release(); + } + else if (hr == S_FALSE) + { + NonStandardLog("NetFlt is not installed currently\n"); + } + else + { + NonStandardLogFlow(("FindComponent failed, hr (0x%x)\n", hr)); + } + + VBoxDrvCfgInfUninstallAllF(L"NetService", VBOXNETCFGWIN_NETFLT_ID, InfRmFlags); + VBoxDrvCfgInfUninstallAllF(L"Net", VBOXNETCFGWIN_NETFLT_MP_ID, InfRmFlags); + + return hr; +} + +VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinNetFltUninstall(IN INetCfg *pNc) +{ + return vboxNetCfgWinNetFltUninstall(pNc, 0); +} + +VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinNetFltInstall(IN INetCfg *pNc, + IN LPCWSTR const *apInfFullPaths, IN UINT cInfFullPaths) +{ + HRESULT hr = vboxNetCfgWinNetFltUninstall(pNc, SUOI_FORCEDELETE); + if (SUCCEEDED(hr)) + { + NonStandardLog("NetFlt will be installed ...\n"); + hr = vboxNetCfgWinInstallInfAndComponent(pNc, VBOXNETCFGWIN_NETFLT_ID, + &GUID_DEVCLASS_NETSERVICE, + apInfFullPaths, + cInfFullPaths, + NULL); + } + return hr; +} + +static HRESULT vboxNetCfgWinNetAdpUninstall(IN INetCfg *pNc, LPCWSTR pwszId, DWORD InfRmFlags) +{ + NOREF(pNc); + NonStandardLog("Finding NetAdp driver package and trying to uninstall it ...\n"); + + VBoxDrvCfgInfUninstallAllF(L"Net", pwszId, InfRmFlags); + NonStandardLog("NetAdp is not installed currently\n"); + return S_OK; +} + +VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinNetAdpUninstall(IN INetCfg *pNc, IN LPCWSTR pwszId) +{ + return vboxNetCfgWinNetAdpUninstall(pNc, pwszId, SUOI_FORCEDELETE); +} + +VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinNetAdpInstall(IN INetCfg *pNc, + IN LPCWSTR const pInfFullPath) +{ + NonStandardLog("NetAdp will be installed ...\n"); + HRESULT hr = vboxNetCfgWinInstallInfAndComponent(pNc, VBOXNETCFGWIN_NETADP_ID, + &GUID_DEVCLASS_NET, + &pInfFullPath, + 1, + NULL); + return hr; +} + +#define VBOXNETCFGWIN_NETLWF_ID L"oracle_VBoxNetLwf" + +static HRESULT vboxNetCfgWinNetLwfUninstall(IN INetCfg *pNc, DWORD InfRmFlags) +{ + INetCfgComponent * pNcc = NULL; + HRESULT hr = pNc->FindComponent(VBOXNETCFGWIN_NETLWF_ID, &pNcc); + if (hr == S_OK) + { + NonStandardLog("NetLwf is installed currently, uninstalling ...\n"); + + hr = VBoxNetCfgWinUninstallComponent(pNc, pNcc); + + pNcc->Release(); + } + else if (hr == S_FALSE) + { + NonStandardLog("NetLwf is not installed currently\n"); + hr = S_OK; + } + else + { + NonStandardLogFlow(("FindComponent failed, hr (0x%x)\n", hr)); + hr = S_OK; + } + + VBoxDrvCfgInfUninstallAllF(L"NetService", VBOXNETCFGWIN_NETLWF_ID, InfRmFlags); + + return hr; +} + +VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinNetLwfUninstall(IN INetCfg *pNc) +{ + return vboxNetCfgWinNetLwfUninstall(pNc, 0); +} + +static void VBoxNetCfgWinFilterLimitWorkaround(void) +{ + /* + * Need to check if the system has a limit of installed filter drivers. If it + * has, bump the limit to 14, which the maximum value supported by Windows 7. + * Note that we only touch the limit if it is set to the default value (8). + * See @bugref{7899}. + */ + HKEY hNetKey; + DWORD dwMaxNumFilters = 0; + DWORD cbMaxNumFilters = sizeof(dwMaxNumFilters); + LONG hr = RegOpenKeyEx(HKEY_LOCAL_MACHINE, + _T("SYSTEM\\CurrentControlSet\\Control\\Network"), + 0, KEY_QUERY_VALUE | KEY_SET_VALUE, &hNetKey); + if (SUCCEEDED(hr)) + { + hr = RegQueryValueEx(hNetKey, _T("MaxNumFilters"), NULL, NULL, + (LPBYTE)&dwMaxNumFilters, &cbMaxNumFilters); + if (SUCCEEDED(hr) && cbMaxNumFilters == sizeof(dwMaxNumFilters) && dwMaxNumFilters == 8) + { + dwMaxNumFilters = 14; + hr = RegSetValueEx(hNetKey, _T("MaxNumFilters"), 0, REG_DWORD, + (LPBYTE)&dwMaxNumFilters, sizeof(dwMaxNumFilters)); + if (SUCCEEDED(hr)) + NonStandardLog("Adjusted the installed filter limit to 14...\n"); + else + NonStandardLog("Failed to set MaxNumFilters, error code 0x%x\n", hr); + } + RegCloseKey(hNetKey); + } + else + { + NonStandardLog("Failed to open network key, error code 0x%x\n", hr); + } + +} + +VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinNetLwfInstall(IN INetCfg *pNc, + IN LPCWSTR const pInfFullPath) +{ + HRESULT hr = vboxNetCfgWinNetLwfUninstall(pNc, SUOI_FORCEDELETE); + if (SUCCEEDED(hr)) + { + VBoxNetCfgWinFilterLimitWorkaround(); + NonStandardLog("NetLwf will be installed ...\n"); + hr = vboxNetCfgWinInstallInfAndComponent(pNc, VBOXNETCFGWIN_NETLWF_ID, + &GUID_DEVCLASS_NETSERVICE, + &pInfFullPath, + 1, + NULL); + } + return hr; +} + +#define VBOX_CONNECTION_NAME L"VirtualBox Host-Only Network" +VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinGenHostonlyConnectionName(PCWSTR DevName, WCHAR *pBuf, PULONG pcbBuf) +{ + const WCHAR * pSuffix = wcsrchr( DevName, L'#' ); + ULONG cbSize = sizeof(VBOX_CONNECTION_NAME); + + if (pSuffix) + { + cbSize += (ULONG)wcslen(pSuffix) * 2; + cbSize += 2; /* for space */ + } + + if (*pcbBuf < cbSize) + { + *pcbBuf = cbSize; + return E_FAIL; + } + + wcscpy(pBuf, VBOX_CONNECTION_NAME); + if (pSuffix) + { + wcscat(pBuf, L" "); + wcscat(pBuf, pSuffix); + } + + return S_OK; +} + +static BOOL vboxNetCfgWinAdjustHostOnlyNetworkInterfacePriority(IN INetCfg *pNc, IN INetCfgComponent *pNcc, PVOID pContext) +{ + RT_NOREF1(pNc); + INetCfgComponentBindings *pNetCfgBindings; + GUID *pGuid = (GUID*)pContext; + + /* Get component's binding. */ + HRESULT hr = pNcc->QueryInterface(IID_INetCfgComponentBindings, (PVOID*)&pNetCfgBindings); + if (SUCCEEDED(hr)) + { + /* Get binding path enumerator reference. */ + IEnumNetCfgBindingPath *pEnumNetCfgBindPath; + hr = pNetCfgBindings->EnumBindingPaths(EBP_BELOW, &pEnumNetCfgBindPath); + if (SUCCEEDED(hr)) + { + bool bFoundIface = false; + hr = pEnumNetCfgBindPath->Reset(); + do + { + INetCfgBindingPath *pNetCfgBindPath; + hr = pEnumNetCfgBindPath->Next(1, &pNetCfgBindPath, NULL); + if (hr == S_OK) + { + IEnumNetCfgBindingInterface *pEnumNetCfgBindIface; + hr = pNetCfgBindPath->EnumBindingInterfaces(&pEnumNetCfgBindIface); + if (hr == S_OK) + { + pEnumNetCfgBindIface->Reset(); + do + { + INetCfgBindingInterface *pNetCfgBindIfce; + hr = pEnumNetCfgBindIface->Next(1, &pNetCfgBindIfce, NULL); + if (hr == S_OK) + { + INetCfgComponent *pNetCfgCompo; + hr = pNetCfgBindIfce->GetLowerComponent(&pNetCfgCompo); + if (hr == S_OK) + { + ULONG uComponentStatus; + hr = pNetCfgCompo->GetDeviceStatus(&uComponentStatus); + if (hr == S_OK) + { + GUID guid; + hr = pNetCfgCompo->GetInstanceGuid(&guid); + if ( hr == S_OK + && guid == *pGuid) + { + hr = pNetCfgBindings->MoveAfter(pNetCfgBindPath, NULL); + if (FAILED(hr)) + NonStandardLogFlow(("Unable to move interface, hr (0x%x)\n", hr)); + bFoundIface = true; + /* + * Enable binding paths for host-only adapters bound to bridged filter + * (see @bugref{8140}). + */ + HRESULT hr2; + LPWSTR pwszHwId = NULL; + if ((hr2 = pNcc->GetId(&pwszHwId)) != S_OK) + NonStandardLogFlow(("Failed to get HW ID, hr (0x%x)\n", hr2)); + else if (_wcsnicmp(pwszHwId, VBOXNETCFGWIN_NETLWF_ID, + sizeof(VBOXNETCFGWIN_NETLWF_ID)/2)) + NonStandardLogFlow(("Ignoring component %ls\n", pwszHwId)); + else if ((hr2 = pNetCfgBindPath->IsEnabled()) != S_FALSE) + NonStandardLogFlow(("Already enabled binding path, hr (0x%x)\n", hr2)); + else if ((hr2 = pNetCfgBindPath->Enable(TRUE)) != S_OK) + NonStandardLogFlow(("Failed to enable binding path, hr (0x%x)\n", hr2)); + else + NonStandardLogFlow(("Enabled binding path\n")); + if (pwszHwId) + CoTaskMemFree(pwszHwId); + } + } + pNetCfgCompo->Release(); + } + else + NonStandardLogFlow(("GetLowerComponent failed, hr (0x%x)\n", hr)); + pNetCfgBindIfce->Release(); + } + else + { + if (hr == S_FALSE) /* No more binding interfaces? */ + hr = S_OK; + else + NonStandardLogFlow(("Next binding interface failed, hr (0x%x)\n", hr)); + break; + } + } while (!bFoundIface); + pEnumNetCfgBindIface->Release(); + } + else + NonStandardLogFlow(("EnumBindingInterfaces failed, hr (0x%x)\n", hr)); + pNetCfgBindPath->Release(); + } + else + { + if (hr == S_FALSE) /* No more binding paths? */ + hr = S_OK; + else + NonStandardLogFlow(("Next bind path failed, hr (0x%x)\n", hr)); + break; + } + } while (!bFoundIface); + pEnumNetCfgBindPath->Release(); + } + else + NonStandardLogFlow(("EnumBindingPaths failed, hr (0x%x)\n", hr)); + pNetCfgBindings->Release(); + } + else + NonStandardLogFlow(("QueryInterface for IID_INetCfgComponentBindings failed, hr (0x%x)\n", hr)); + return TRUE; +} + +static UINT WINAPI vboxNetCfgWinPspFileCallback( + PVOID Context, + UINT Notification, + UINT_PTR Param1, + UINT_PTR Param2 + ) +{ + switch (Notification) + { + case SPFILENOTIFY_TARGETNEWER: + case SPFILENOTIFY_TARGETEXISTS: + return TRUE; + } + return SetupDefaultQueueCallback(Context, Notification, Param1, Param2); +} + +/* The original source of the VBoxNetAdp adapter creation/destruction code has the following copyright */ +/* + Copyright 2004 by the Massachusetts Institute of Technology + + All rights reserved. + + Permission to use, copy, modify, and distribute this software and its + documentation for any purpose and without fee is hereby granted, + provided that the above copyright notice appear in all copies and that + both that copyright notice and this permission notice appear in + supporting documentation, and that the name of the Massachusetts + Institute of Technology (M.I.T.) not be used in advertising or publicity + pertaining to distribution of the software without specific, written + prior permission. + + M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING + ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL + M.I.T. BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR + ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, + ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + SOFTWARE. +*/ + + +/** + * Use the IShellFolder API to rename the connection. + */ +static HRESULT rename_shellfolder (PCWSTR wGuid, PCWSTR wNewName) +{ + /* This is the GUID for the network connections folder. It is constant. + * {7007ACC7-3202-11D1-AAD2-00805FC1270E} */ + const GUID CLSID_NetworkConnections = { + 0x7007ACC7, 0x3202, 0x11D1, { + 0xAA, 0xD2, 0x00, 0x80, 0x5F, 0xC1, 0x27, 0x0E + } + }; + + LPITEMIDLIST pidl = NULL; + IShellFolder *pShellFolder = NULL; + HRESULT hr; + + /* Build the display name in the form "::{GUID}". */ + if (wcslen(wGuid) >= MAX_PATH) + return E_INVALIDARG; + WCHAR szAdapterGuid[MAX_PATH + 2] = {0}; + swprintf(szAdapterGuid, L"::%ls", wGuid); + + /* Create an instance of the network connections folder. */ + hr = CoCreateInstance(CLSID_NetworkConnections, NULL, + CLSCTX_INPROC_SERVER, IID_IShellFolder, + reinterpret_cast<LPVOID *>(&pShellFolder)); + /* Parse the display name. */ + if (SUCCEEDED (hr)) + { + hr = pShellFolder->ParseDisplayName (NULL, NULL, szAdapterGuid, NULL, + &pidl, NULL); + } + if (SUCCEEDED (hr)) + { + hr = pShellFolder->SetNameOf (NULL, pidl, wNewName, SHGDN_NORMAL, + &pidl); + } + + CoTaskMemFree (pidl); + + if (pShellFolder) + pShellFolder->Release(); + + return hr; +} + +/** + * Loads a system DLL. + * + * @returns Module handle or NULL + * @param pszName The DLL name. + */ +static HMODULE loadSystemDll(const char *pszName) +{ + char szPath[MAX_PATH]; + UINT cchPath = GetSystemDirectoryA(szPath, sizeof(szPath)); + size_t cbName = strlen(pszName) + 1; + if (cchPath + 1 + cbName > sizeof(szPath)) + return NULL; + szPath[cchPath] = '\\'; + memcpy(&szPath[cchPath + 1], pszName, cbName); + return LoadLibraryA(szPath); +} + +VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinRenameConnection (LPWSTR pGuid, PCWSTR NewName) +{ + typedef HRESULT (WINAPI *lpHrRenameConnection) (const GUID *, PCWSTR); + lpHrRenameConnection RenameConnectionFunc = NULL; + HRESULT status; + + /* First try the IShellFolder interface, which was unimplemented + * for the network connections folder before XP. */ + status = rename_shellfolder (pGuid, NewName); + if (status == E_NOTIMPL) + { +/** @todo that code doesn't seem to work! */ + /* The IShellFolder interface is not implemented on this platform. + * Try the (undocumented) HrRenameConnection API in the netshell + * library. */ + CLSID clsid; + HINSTANCE hNetShell; + status = CLSIDFromString ((LPOLESTR) pGuid, &clsid); + if (FAILED(status)) + return E_FAIL; + hNetShell = loadSystemDll("netshell.dll"); + if (hNetShell == NULL) + return E_FAIL; + RenameConnectionFunc = + (lpHrRenameConnection) GetProcAddress (hNetShell, + "HrRenameConnection"); + if (RenameConnectionFunc == NULL) + { + FreeLibrary (hNetShell); + return E_FAIL; + } + status = RenameConnectionFunc (&clsid, NewName); + FreeLibrary (hNetShell); + } + if (FAILED (status)) + return status; + + return S_OK; +} + +#define DRIVERHWID _T("sun_VBoxNetAdp") + +#define SetErrBreak(strAndArgs) \ + if (1) { \ + hrc = E_FAIL; \ + NonStandardLog strAndArgs; \ + bstrError = bstr_printf strAndArgs; \ + break; \ + } else do {} while (0) + +VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinRemoveHostOnlyNetworkInterface(IN const GUID *pGUID, OUT BSTR *pErrMsg) +{ + HRESULT hrc = S_OK; + bstr_t bstrError; + + do + { + TCHAR lszPnPInstanceId [512] = {0}; + + /* We have to find the device instance ID through a registry search */ + + HKEY hkeyNetwork = 0; + HKEY hkeyConnection = 0; + + do + { + WCHAR strRegLocation [256]; + WCHAR wszGuid[50]; + + int length = StringFromGUID2(*pGUID, wszGuid, RT_ELEMENTS(wszGuid)); + if (!length) + SetErrBreak(("Failed to create a Guid string")); + + swprintf (strRegLocation, + L"SYSTEM\\CurrentControlSet\\Control\\Network\\" + L"{4D36E972-E325-11CE-BFC1-08002BE10318}\\%s", + wszGuid); + + LONG status; + status = RegOpenKeyExW (HKEY_LOCAL_MACHINE, strRegLocation, 0, + KEY_READ, &hkeyNetwork); + if ((status != ERROR_SUCCESS) || !hkeyNetwork) + SetErrBreak (("Host interface network is not found in registry (%S) [1]", + strRegLocation)); + + status = RegOpenKeyExW (hkeyNetwork, L"Connection", 0, + KEY_READ, &hkeyConnection); + if ((status != ERROR_SUCCESS) || !hkeyConnection) + SetErrBreak (("Host interface network is not found in registry (%S) [2]", + strRegLocation)); + + DWORD len = sizeof (lszPnPInstanceId); + DWORD dwKeyType; + status = RegQueryValueExW (hkeyConnection, L"PnPInstanceID", NULL, + &dwKeyType, (LPBYTE) lszPnPInstanceId, &len); + if ((status != ERROR_SUCCESS) || (dwKeyType != REG_SZ)) + SetErrBreak (("Host interface network is not found in registry (%S) [3]", + strRegLocation)); + } + while (0); + + if (hkeyConnection) + RegCloseKey (hkeyConnection); + if (hkeyNetwork) + RegCloseKey (hkeyNetwork); + + if (FAILED (hrc)) + break; + + /* + * Now we are going to enumerate all network devices and + * wait until we encounter the right device instance ID + */ + + HDEVINFO hDeviceInfo = INVALID_HANDLE_VALUE; + + do + { + BOOL ok; + GUID netGuid; + SP_DEVINFO_DATA DeviceInfoData; + DWORD index = 0; + BOOL found = FALSE; + DWORD size = 0; + + /* initialize the structure size */ + DeviceInfoData.cbSize = sizeof (SP_DEVINFO_DATA); + + /* copy the net class GUID */ + memcpy(&netGuid, &GUID_DEVCLASS_NET, sizeof (GUID_DEVCLASS_NET)); + + /* return a device info set contains all installed devices of the Net class */ + hDeviceInfo = SetupDiGetClassDevs(&netGuid, NULL, NULL, DIGCF_PRESENT); + + if (hDeviceInfo == INVALID_HANDLE_VALUE) + SetErrBreak(("SetupDiGetClassDevs failed (0x%08X)", GetLastError())); + + /* enumerate the driver info list */ + while (TRUE) + { + TCHAR *deviceHwid; + + ok = SetupDiEnumDeviceInfo(hDeviceInfo, index, &DeviceInfoData); + + if (!ok) + { + if (GetLastError() == ERROR_NO_MORE_ITEMS) + break; + else + { + index++; + continue; + } + } + + /* try to get the hardware ID registry property */ + ok = SetupDiGetDeviceRegistryProperty(hDeviceInfo, + &DeviceInfoData, + SPDRP_HARDWAREID, + NULL, + NULL, + 0, + &size); + if (!ok) + { + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) + { + index++; + continue; + } + + deviceHwid = (TCHAR *) malloc(size); + ok = SetupDiGetDeviceRegistryProperty(hDeviceInfo, + &DeviceInfoData, + SPDRP_HARDWAREID, + NULL, + (PBYTE)deviceHwid, + size, + NULL); + if (!ok) + { + free(deviceHwid); + deviceHwid = NULL; + index++; + continue; + } + } + else + { + /* something is wrong. This shouldn't have worked with a NULL buffer */ + index++; + continue; + } + + for (TCHAR *t = deviceHwid; + t && *t && t < &deviceHwid[size / sizeof(TCHAR)]; + t += _tcslen(t) + 1) + { + if (!_tcsicmp(DRIVERHWID, t)) + { + /* get the device instance ID */ + TCHAR devId[MAX_DEVICE_ID_LEN]; + if (CM_Get_Device_ID(DeviceInfoData.DevInst, + devId, MAX_DEVICE_ID_LEN, 0) == CR_SUCCESS) + { + /* compare to what we determined before */ + if (wcscmp(devId, lszPnPInstanceId) == 0) + { + found = TRUE; + break; + } + } + } + } + + if (deviceHwid) + { + free (deviceHwid); + deviceHwid = NULL; + } + + if (found) + break; + + index++; + } + + if (found == FALSE) + SetErrBreak (("Host Interface Network driver not found (0x%08X)", + GetLastError())); + + ok = SetupDiSetSelectedDevice (hDeviceInfo, &DeviceInfoData); + if (!ok) + SetErrBreak (("SetupDiSetSelectedDevice failed (0x%08X)", + GetLastError())); + + ok = SetupDiCallClassInstaller (DIF_REMOVE, hDeviceInfo, &DeviceInfoData); + if (!ok) + SetErrBreak (("SetupDiCallClassInstaller (DIF_REMOVE) failed (0x%08X)", + GetLastError())); + } + while (0); + + /* clean up the device info set */ + if (hDeviceInfo != INVALID_HANDLE_VALUE) + SetupDiDestroyDeviceInfoList (hDeviceInfo); + + if (FAILED (hrc)) + break; + } + while (0); + + if (pErrMsg && bstrError.length()) + *pErrMsg = bstrError.Detach(); + + return hrc; +} + +VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinUpdateHostOnlyNetworkInterface(LPCWSTR pcsxwInf, BOOL *pbRebootRequired, LPCWSTR pcsxwId) +{ + return VBoxDrvCfgDrvUpdate(pcsxwId, pcsxwInf, pbRebootRequired); +} + +static const char *vboxNetCfgWinGetStateText(DWORD dwState) +{ + switch (dwState) + { + case SERVICE_STOPPED: return "is not running"; + case SERVICE_STOP_PENDING: return "is stopping"; + case SERVICE_CONTINUE_PENDING: return "continue is pending"; + case SERVICE_PAUSE_PENDING: return "pause is pending"; + case SERVICE_PAUSED: return "is paused"; + case SERVICE_RUNNING: return "is running"; + case SERVICE_START_PENDING: return "is starting"; + } + return "state is invalid"; +} + +static DWORD vboxNetCfgWinGetNetSetupState(SC_HANDLE hService) +{ + SERVICE_STATUS status; + status.dwCurrentState = SERVICE_RUNNING; + if (hService) { + if (QueryServiceStatus(hService, &status)) + NonStandardLogFlow(("NetSetupSvc %s\n", vboxNetCfgWinGetStateText(status.dwCurrentState))); + else + NonStandardLogFlow(("QueryServiceStatus failed (0x%x)\n", GetLastError())); + } + return status.dwCurrentState; +} + +DECLINLINE(bool) vboxNetCfgWinIsNetSetupRunning(SC_HANDLE hService) +{ + return vboxNetCfgWinGetNetSetupState(hService) == SERVICE_RUNNING; +} + +DECLINLINE(bool) vboxNetCfgWinIsNetSetupStopped(SC_HANDLE hService) +{ + return vboxNetCfgWinGetNetSetupState(hService) == SERVICE_STOPPED; +} + +static HRESULT vboxNetCfgWinCreateHostOnlyNetworkInterface(IN LPCWSTR pInfPath, IN bool bIsInfPathFile, + OUT GUID *pGuid, OUT BSTR *lppszName, OUT BSTR *pErrMsg) +{ + HRESULT hrc = S_OK; + + HDEVINFO hDeviceInfo = INVALID_HANDLE_VALUE; + SP_DEVINFO_DATA DeviceInfoData; + PVOID pQueueCallbackContext = NULL; + DWORD ret = 0; + BOOL registered = FALSE; + BOOL destroyList = FALSE; + WCHAR pWCfgGuidString [50]; + WCHAR DevName[256]; + HKEY hkey = (HKEY)INVALID_HANDLE_VALUE; + bstr_t bstrError; + + do + { + BOOL found = FALSE; + GUID netGuid; + SP_DRVINFO_DATA DriverInfoData; + SP_DEVINSTALL_PARAMS DeviceInstallParams; + TCHAR className [MAX_PATH]; + DWORD index = 0; + PSP_DRVINFO_DETAIL_DATA pDriverInfoDetail; + /* for our purposes, 2k buffer is more + * than enough to obtain the hardware ID + * of the VBoxNetAdp driver. */ + DWORD detailBuf [2048]; + + DWORD cbSize; + DWORD dwValueType; + + /* initialize the structure size */ + DeviceInfoData.cbSize = sizeof (SP_DEVINFO_DATA); + DriverInfoData.cbSize = sizeof (SP_DRVINFO_DATA); + + /* copy the net class GUID */ + memcpy(&netGuid, &GUID_DEVCLASS_NET, sizeof(GUID_DEVCLASS_NET)); + + /* create an empty device info set associated with the net class GUID */ + hDeviceInfo = SetupDiCreateDeviceInfoList(&netGuid, NULL); + if (hDeviceInfo == INVALID_HANDLE_VALUE) + SetErrBreak (("SetupDiCreateDeviceInfoList failed (0x%08X)", + GetLastError())); + + /* get the class name from GUID */ + BOOL fResult = SetupDiClassNameFromGuid (&netGuid, className, MAX_PATH, NULL); + if (!fResult) + SetErrBreak (("SetupDiClassNameFromGuid failed (0x%08X)", + GetLastError())); + + /* create a device info element and add the new device instance + * key to registry */ + fResult = SetupDiCreateDeviceInfo (hDeviceInfo, className, &netGuid, NULL, NULL, + DICD_GENERATE_ID, &DeviceInfoData); + if (!fResult) + SetErrBreak (("SetupDiCreateDeviceInfo failed (0x%08X)", + GetLastError())); + + /* select the newly created device info to be the currently + selected member */ + fResult = SetupDiSetSelectedDevice (hDeviceInfo, &DeviceInfoData); + if (!fResult) + SetErrBreak (("SetupDiSetSelectedDevice failed (0x%08X)", + GetLastError())); + + if (pInfPath) + { + /* get the device install parameters and disable filecopy */ + DeviceInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS); + fResult = SetupDiGetDeviceInstallParams (hDeviceInfo, &DeviceInfoData, + &DeviceInstallParams); + if (fResult) + { + memset(DeviceInstallParams.DriverPath, 0, sizeof(DeviceInstallParams.DriverPath)); + size_t pathLenght = wcslen(pInfPath) + 1/* null terminator */; + if (pathLenght < sizeof(DeviceInstallParams.DriverPath)/sizeof(DeviceInstallParams.DriverPath[0])) + { + memcpy(DeviceInstallParams.DriverPath, pInfPath, pathLenght*sizeof(DeviceInstallParams.DriverPath[0])); + + if (bIsInfPathFile) + { + DeviceInstallParams.Flags |= DI_ENUMSINGLEINF; + } + + fResult = SetupDiSetDeviceInstallParams(hDeviceInfo, &DeviceInfoData, + &DeviceInstallParams); + if (!fResult) + { + DWORD winEr = GetLastError(); + NonStandardLogFlow(("SetupDiSetDeviceInstallParams failed, winEr (%d)\n", winEr)); + break; + } + } + else + { + NonStandardLogFlow(("SetupDiSetDeviceInstallParams faileed: INF path is too long\n")); + break; + } + } + else + { + DWORD winEr = GetLastError(); + NonStandardLogFlow(("SetupDiGetDeviceInstallParams failed, winEr (%d)\n", winEr)); + } + } + + /* build a list of class drivers */ + fResult = SetupDiBuildDriverInfoList (hDeviceInfo, &DeviceInfoData, + SPDIT_CLASSDRIVER); + if (!fResult) + SetErrBreak (("SetupDiBuildDriverInfoList failed (0x%08X)", + GetLastError())); + + destroyList = TRUE; + + /* enumerate the driver info list */ + while (TRUE) + { + BOOL ret; + + ret = SetupDiEnumDriverInfo (hDeviceInfo, &DeviceInfoData, + SPDIT_CLASSDRIVER, index, &DriverInfoData); + + /* if the function failed and GetLastError() returned + * ERROR_NO_MORE_ITEMS, then we have reached the end of the + * list. Otherwise there was something wrong with this + * particular driver. */ + if (!ret) + { + if (GetLastError() == ERROR_NO_MORE_ITEMS) + break; + else + { + index++; + continue; + } + } + + pDriverInfoDetail = (PSP_DRVINFO_DETAIL_DATA) detailBuf; + pDriverInfoDetail->cbSize = sizeof(SP_DRVINFO_DETAIL_DATA); + + /* if we successfully find the hardware ID and it turns out to + * be the one for the loopback driver, then we are done. */ + if (SetupDiGetDriverInfoDetail (hDeviceInfo, + &DeviceInfoData, + &DriverInfoData, + pDriverInfoDetail, + sizeof (detailBuf), + NULL)) + { + TCHAR * t; + + /* pDriverInfoDetail->HardwareID is a MULTISZ string. Go through the + * whole list and see if there is a match somewhere. */ + t = pDriverInfoDetail->HardwareID; + while (t && *t && t < (TCHAR *) &detailBuf [RT_ELEMENTS(detailBuf)]) + { + if (!_tcsicmp(t, DRIVERHWID)) + break; + + t += _tcslen(t) + 1; + } + + if (t && *t && t < (TCHAR *) &detailBuf [RT_ELEMENTS(detailBuf)]) + { + found = TRUE; + break; + } + } + + index ++; + } + + if (!found) + SetErrBreak(("Could not find Host Interface Networking driver! Please reinstall")); + + /* set the loopback driver to be the currently selected */ + fResult = SetupDiSetSelectedDriver (hDeviceInfo, &DeviceInfoData, + &DriverInfoData); + if (!fResult) + SetErrBreak(("SetupDiSetSelectedDriver failed (0x%08X)", + GetLastError())); + + /* register the phantom device to prepare for install */ + fResult = SetupDiCallClassInstaller (DIF_REGISTERDEVICE, hDeviceInfo, + &DeviceInfoData); + if (!fResult) + { + DWORD err = GetLastError(); + SetErrBreak (("SetupDiCallClassInstaller failed (0x%08X)", + err)); + } + + /* registered, but remove if errors occur in the following code */ + registered = TRUE; + + /* ask the installer if we can install the device */ + fResult = SetupDiCallClassInstaller (DIF_ALLOW_INSTALL, hDeviceInfo, + &DeviceInfoData); + if (!fResult) + { + if (GetLastError() != ERROR_DI_DO_DEFAULT) + SetErrBreak (("SetupDiCallClassInstaller (DIF_ALLOW_INSTALL) failed (0x%08X)", + GetLastError())); + /* that's fine */ + } + + /* get the device install parameters and disable filecopy */ + DeviceInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS); + fResult = SetupDiGetDeviceInstallParams (hDeviceInfo, &DeviceInfoData, + &DeviceInstallParams); + if (fResult) + { + pQueueCallbackContext = SetupInitDefaultQueueCallback(NULL); + if (pQueueCallbackContext) + { + DeviceInstallParams.InstallMsgHandlerContext = pQueueCallbackContext; + DeviceInstallParams.InstallMsgHandler = (PSP_FILE_CALLBACK)vboxNetCfgWinPspFileCallback; + fResult = SetupDiSetDeviceInstallParams (hDeviceInfo, &DeviceInfoData, + &DeviceInstallParams); + if (!fResult) + { + DWORD winEr = GetLastError(); + NonStandardLogFlow(("SetupDiSetDeviceInstallParams failed, winEr (%d)\n", winEr)); + } + Assert(fResult); + } + else + { + DWORD winEr = GetLastError(); + NonStandardLogFlow(("SetupInitDefaultQueueCallback failed, winEr (%d)\n", winEr)); + } + } + else + { + DWORD winEr = GetLastError(); + NonStandardLogFlow(("SetupDiGetDeviceInstallParams failed, winEr (%d)\n", winEr)); + } + + /* install the files first */ + fResult = SetupDiCallClassInstaller (DIF_INSTALLDEVICEFILES, hDeviceInfo, + &DeviceInfoData); + if (!fResult) + SetErrBreak (("SetupDiCallClassInstaller (DIF_INSTALLDEVICEFILES) failed (0x%08X)", + GetLastError())); + /* get the device install parameters and disable filecopy */ + DeviceInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS); + fResult = SetupDiGetDeviceInstallParams (hDeviceInfo, &DeviceInfoData, + &DeviceInstallParams); + if (fResult) + { + DeviceInstallParams.Flags |= DI_NOFILECOPY; + fResult = SetupDiSetDeviceInstallParams(hDeviceInfo, &DeviceInfoData, + &DeviceInstallParams); + if (!fResult) + SetErrBreak (("SetupDiSetDeviceInstallParams failed (0x%08X)", + GetLastError())); + } + + /* + * Register any device-specific co-installers for this device, + */ + fResult = SetupDiCallClassInstaller(DIF_REGISTER_COINSTALLERS, + hDeviceInfo, + &DeviceInfoData); + if (!fResult) + SetErrBreak (("SetupDiCallClassInstaller (DIF_REGISTER_COINSTALLERS) failed (0x%08X)", + GetLastError())); + + /* + * install any installer-specified interfaces. + * and then do the real install + */ + fResult = SetupDiCallClassInstaller(DIF_INSTALLINTERFACES, + hDeviceInfo, + &DeviceInfoData); + if (!fResult) + SetErrBreak (("SetupDiCallClassInstaller (DIF_INSTALLINTERFACES) failed (0x%08X)", + GetLastError())); + + fResult = SetupDiCallClassInstaller(DIF_INSTALLDEVICE, + hDeviceInfo, + &DeviceInfoData); + if (!fResult) + SetErrBreak (("SetupDiCallClassInstaller (DIF_INSTALLDEVICE) failed (0x%08X)", + GetLastError())); + + /* Query the instance ID; on Windows 10, the registry key may take a short + * while to appear. Microsoft recommends waiting for up to 5 seconds, but + * we want to be on the safe side, so let's wait for 20 seconds. Waiting + * longer is harmful as network setup service will shut down after a period + * of inactivity. + */ + for (int retries = 0; retries < 2 * 20; ++retries) + { + Sleep(500); /* half second */ + + /* Figure out NetCfgInstanceId */ + hkey = SetupDiOpenDevRegKey(hDeviceInfo, + &DeviceInfoData, + DICS_FLAG_GLOBAL, + 0, + DIREG_DRV, + KEY_READ); + if (hkey == INVALID_HANDLE_VALUE) + break; + + cbSize = sizeof(pWCfgGuidString); + ret = RegQueryValueExW (hkey, L"NetCfgInstanceId", NULL, + &dwValueType, (LPBYTE) pWCfgGuidString, &cbSize); + /* As long as the return code is FILE_NOT_FOUND, sleep and retry. */ + if (ret != ERROR_FILE_NOT_FOUND) + break; + + RegCloseKey (hkey); + hkey = (HKEY)INVALID_HANDLE_VALUE; + } + + if (ret == ERROR_FILE_NOT_FOUND) + { + hrc = E_ABORT; + break; + } + + /* + * We need to check 'hkey' after we check 'ret' to distinguish the case + * of failed SetupDiOpenDevRegKey from the case when we timed out. + */ + if (hkey == INVALID_HANDLE_VALUE) + SetErrBreak(("SetupDiOpenDevRegKey failed (0x%08X)", GetLastError())); + + if (ret != ERROR_SUCCESS) + SetErrBreak(("Querying NetCfgInstanceId failed (0x%08X)", ret)); + + NET_LUID luid; + HRESULT hSMRes = vboxNetCfgWinGetInterfaceLUID(hkey, &luid); + + /* Close the key as soon as possible. See @bugref{7973}. */ + RegCloseKey (hkey); + hkey = (HKEY)INVALID_HANDLE_VALUE; + + if (FAILED(hSMRes)) + { + /* + * The setting of Metric is not very important functionality, + * So we will not break installation process due to this error. + */ + NonStandardLogFlow(("vboxNetCfgWinCreateHostOnlyNetworkInterface Warning! " + "vboxNetCfgWinGetInterfaceLUID failed, default metric " + "for new interface will not be set, hr (0x%x)\n", hSMRes)); + } + else + { + /* + * Set default metric value of interface to fix multicast issue + * See @bugref{6379} for details. + */ + hSMRes = vboxNetCfgWinSetupMetric(&luid); + if (FAILED(hSMRes)) + { + /* + * The setting of Metric is not very important functionality, + * So we will not break installation process due to this error. + */ + NonStandardLogFlow(("vboxNetCfgWinCreateHostOnlyNetworkInterface Warning! " + "vboxNetCfgWinSetupMetric failed, default metric " + "for new interface will not be set, hr (0x%x)\n", hSMRes)); + } + } + + +#ifndef VBOXNETCFG_DELAYEDRENAME + /* + * We need to query the device name after we have succeeded in querying its + * instance ID to avoid similar waiting-and-retrying loop (see @bugref{7973}). + */ + if (!SetupDiGetDeviceRegistryPropertyW(hDeviceInfo, &DeviceInfoData, + SPDRP_FRIENDLYNAME , /* IN DWORD Property,*/ + NULL, /*OUT PDWORD PropertyRegDataType, OPTIONAL*/ + (PBYTE)DevName, /*OUT PBYTE PropertyBuffer,*/ + sizeof(DevName), /* IN DWORD PropertyBufferSize,*/ + NULL /*OUT PDWORD RequiredSize OPTIONAL*/)) + { + int err = GetLastError(); + if (err != ERROR_INVALID_DATA) + { + SetErrBreak (("SetupDiGetDeviceRegistryProperty failed (0x%08X)", + err)); + } + + if (!SetupDiGetDeviceRegistryPropertyW(hDeviceInfo, &DeviceInfoData, + SPDRP_DEVICEDESC, /* IN DWORD Property,*/ + NULL, /*OUT PDWORD PropertyRegDataType, OPTIONAL*/ + (PBYTE)DevName, /*OUT PBYTE PropertyBuffer,*/ + sizeof(DevName), /* IN DWORD PropertyBufferSize,*/ + NULL /*OUT PDWORD RequiredSize OPTIONAL*/ + )) + { + err = GetLastError(); + SetErrBreak (("SetupDiGetDeviceRegistryProperty failed (0x%08X)", + err)); + } + } +#else /* !VBOXNETCFG_DELAYEDRENAME */ + /* Re-use DevName for device instance id retrieval. */ + if (!SetupDiGetDeviceInstanceId(hDeviceInfo, &DeviceInfoData, DevName, RT_ELEMENTS(DevName), &cbSize)) + SetErrBreak (("SetupDiGetDeviceInstanceId failed (0x%08X)", + GetLastError())); +#endif /* !VBOXNETCFG_DELAYEDRENAME */ + } + while (0); + + /* + * cleanup + */ + if (hkey != INVALID_HANDLE_VALUE) + RegCloseKey (hkey); + + if (pQueueCallbackContext) + SetupTermDefaultQueueCallback(pQueueCallbackContext); + + if (hDeviceInfo != INVALID_HANDLE_VALUE) + { + /* an error has occurred, but the device is registered, we must remove it */ + if (ret != 0 && registered) + SetupDiCallClassInstaller(DIF_REMOVE, hDeviceInfo, &DeviceInfoData); + + SetupDiDeleteDeviceInfo(hDeviceInfo, &DeviceInfoData); + + /* destroy the driver info list */ + if (destroyList) + SetupDiDestroyDriverInfoList(hDeviceInfo, &DeviceInfoData, + SPDIT_CLASSDRIVER); + /* clean up the device info set */ + SetupDiDestroyDeviceInfoList (hDeviceInfo); + } + + /* return the network connection GUID on success */ + if (SUCCEEDED(hrc)) + { + HRESULT hr; + INetCfg *pNetCfg = NULL; + LPWSTR lpszApp = NULL; +#ifndef VBOXNETCFG_DELAYEDRENAME + WCHAR ConnectionName[128]; + ULONG cbName = sizeof(ConnectionName); + + hr = VBoxNetCfgWinGenHostonlyConnectionName(DevName, ConnectionName, &cbName); + if (SUCCEEDED(hr)) + hr = VBoxNetCfgWinRenameConnection(pWCfgGuidString, ConnectionName); +#endif + if (lppszName) + { + *lppszName = SysAllocString((const OLECHAR *) DevName); + if (!*lppszName) + { + NonStandardLogFlow(("SysAllocString failed\n")); + hrc = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY); + } + } + + if (pGuid) + { + hrc = CLSIDFromString(pWCfgGuidString, (LPCLSID)pGuid); + if (FAILED(hrc)) + NonStandardLogFlow(("CLSIDFromString failed, hrc (0x%x)\n", hrc)); + } + + hr = VBoxNetCfgWinQueryINetCfg(&pNetCfg, TRUE, L"VirtualBox Host-Only Creation", + 30 * 1000, /* on Vista we often get 6to4svc.dll holding the lock, wait for 30 sec. */ + /** @todo special handling for 6to4svc.dll ???, i.e. several retrieves */ + &lpszApp); + if (hr == S_OK) + { + hr = vboxNetCfgWinEnumNetCfgComponents(pNetCfg, + &GUID_DEVCLASS_NETSERVICE, + vboxNetCfgWinAdjustHostOnlyNetworkInterfacePriority, + pGuid); + if (SUCCEEDED(hr)) + { + hr = vboxNetCfgWinEnumNetCfgComponents(pNetCfg, + &GUID_DEVCLASS_NETTRANS, + vboxNetCfgWinAdjustHostOnlyNetworkInterfacePriority, + pGuid); + if (SUCCEEDED(hr)) + hr = vboxNetCfgWinEnumNetCfgComponents(pNetCfg, + &GUID_DEVCLASS_NETCLIENT, + vboxNetCfgWinAdjustHostOnlyNetworkInterfacePriority, + pGuid); + } + + if (SUCCEEDED(hr)) + { + hr = pNetCfg->Apply(); + } + else + NonStandardLogFlow(("Enumeration failed, hr 0x%x\n", hr)); + VBoxNetCfgWinReleaseINetCfg(pNetCfg, TRUE); + } + else if (hr == NETCFG_E_NO_WRITE_LOCK && lpszApp) + { + NonStandardLogFlow(("Application %ws is holding the lock, failed\n", lpszApp)); + CoTaskMemFree(lpszApp); + } + else + NonStandardLogFlow(("VBoxNetCfgWinQueryINetCfg failed, hr 0x%x\n", hr)); + } + + if (pErrMsg && bstrError.length()) + *pErrMsg = bstrError.Detach(); + + return hrc; +} + +VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinCreateHostOnlyNetworkInterface(IN LPCWSTR pInfPath, IN bool bIsInfPathFile, + OUT GUID *pGuid, OUT BSTR *lppszName, OUT BSTR *pErrMsg) +{ + HRESULT hrc = vboxNetCfgWinCreateHostOnlyNetworkInterface(pInfPath, bIsInfPathFile, pGuid, lppszName, pErrMsg); + if (hrc == E_ABORT) + { + NonStandardLogFlow(("Timed out while waiting for NetCfgInstanceId, try again immediately...\n")); + /* + * This is the first time we fail to obtain NetCfgInstanceId, let us + * retry it once. It is needed to handle the situation when network + * setup fails to recognize the arrival of our device node while it + * is busy removing another host-only interface, and it gets stuck + * with no matching network interface created for our device node. + * See @bugref{7973} for details. + */ + hrc = vboxNetCfgWinCreateHostOnlyNetworkInterface(pInfPath, bIsInfPathFile, pGuid, lppszName, pErrMsg); + if (hrc == E_ABORT) + { + NonStandardLogFlow(("Timed out again while waiting for NetCfgInstanceId, try again after a while...\n")); + /* + * This is the second time we fail to obtain NetCfgInstanceId, let us + * retry it once more. This time we wait to network setup service + * to go down before retrying. Hopefully it will resolve all error + * conditions. See @bugref{7973} for details. + */ + + SC_HANDLE hSCM = NULL; + SC_HANDLE hService = NULL; + + hSCM = OpenSCManager(NULL, NULL, GENERIC_READ); + if (hSCM) + { + hService = OpenService(hSCM, _T("NetSetupSvc"), GENERIC_READ); + if (hService) + { + for (int retries = 0; retries < 60 && !vboxNetCfgWinIsNetSetupStopped(hService); ++retries) + Sleep(1000); + CloseServiceHandle(hService); + hrc = vboxNetCfgWinCreateHostOnlyNetworkInterface(pInfPath, bIsInfPathFile, pGuid, lppszName, pErrMsg); + } + else + NonStandardLogFlow(("OpenService failed (0x%x)\n", GetLastError())); + CloseServiceHandle(hSCM); + } + else + NonStandardLogFlow(("OpenSCManager failed (0x%x)", GetLastError())); + /* Give up and report the error. */ + if (hrc == E_ABORT) + { + if (pErrMsg) + { + bstr_t bstrError = bstr_printf("Querying NetCfgInstanceId failed (0x%08X)", ERROR_FILE_NOT_FOUND); + *pErrMsg = bstrError.Detach(); + } + hrc = E_FAIL; + } + } + } + return hrc; +} + + +HRESULT vboxLoadIpHelpFunctions(HINSTANCE& pIpHlpInstance) +{ + Assert(pIpHlpInstance != NULL); + + pIpHlpInstance = loadSystemDll("Iphlpapi.dll"); + if (pIpHlpInstance == NULL) + return E_FAIL; + + g_pfnInitializeIpInterfaceEntry = + (PFNINITIALIZEIPINTERFACEENTRY)GetProcAddress(pIpHlpInstance, "InitializeIpInterfaceEntry"); + Assert(g_pfnInitializeIpInterfaceEntry); + + if (g_pfnInitializeIpInterfaceEntry) + { + g_pfnGetIpInterfaceEntry = + (PFNGETIPINTERFACEENTRY)GetProcAddress(pIpHlpInstance, "GetIpInterfaceEntry"); + Assert(g_pfnGetIpInterfaceEntry); + } + + if (g_pfnGetIpInterfaceEntry) + { + g_pfnSetIpInterfaceEntry = + (PFNSETIPINTERFACEENTRY)GetProcAddress(pIpHlpInstance, "SetIpInterfaceEntry"); + Assert(g_pfnSetIpInterfaceEntry); + } + + if (g_pfnInitializeIpInterfaceEntry == NULL) + { + FreeLibrary(pIpHlpInstance); + pIpHlpInstance = NULL; + return E_FAIL; + } + + return S_OK; +} + + +HRESULT vboxNetCfgWinGetLoopbackMetric(OUT int* Metric) +{ + HRESULT rc = S_OK; + MIB_IPINTERFACE_ROW row; + + Assert(g_pfnInitializeIpInterfaceEntry != NULL); + Assert(g_pfnGetIpInterfaceEntry != NULL); + + g_pfnInitializeIpInterfaceEntry(&row); + row.Family = AF_INET; + row.InterfaceLuid.Info.IfType = IF_TYPE_SOFTWARE_LOOPBACK; + + rc = g_pfnGetIpInterfaceEntry(&row); + if (rc != NO_ERROR) + return HRESULT_FROM_WIN32(rc); + + *Metric = row.Metric; + + return rc; +} + + +HRESULT vboxNetCfgWinSetInterfaceMetric( + IN NET_LUID* pInterfaceLuid, + IN DWORD metric) +{ + MIB_IPINTERFACE_ROW newRow; + + Assert(g_pfnInitializeIpInterfaceEntry != NULL); + Assert(g_pfnSetIpInterfaceEntry != NULL); + + g_pfnInitializeIpInterfaceEntry(&newRow); + // identificate the interface to change + newRow.InterfaceLuid = *pInterfaceLuid; + newRow.Family = AF_INET; + // changed settings + newRow.UseAutomaticMetric = false; + newRow.Metric = metric; + + // change settings + return HRESULT_FROM_WIN32(g_pfnSetIpInterfaceEntry(&newRow)); +} + + +HRESULT vboxNetCfgWinGetInterfaceLUID(IN HKEY hKey, OUT NET_LUID* pLUID) +{ + HRESULT res = S_OK; + DWORD luidIndex = 0; + DWORD ifType = 0; + DWORD cbSize = sizeof(luidIndex); + DWORD dwValueType = REG_DWORD; + + if (pLUID == NULL) + return E_INVALIDARG; + + res = RegQueryValueExW(hKey, L"NetLuidIndex", NULL, + &dwValueType, (LPBYTE)&luidIndex, &cbSize); + if (res != 0) + return HRESULT_FROM_WIN32(res); + + cbSize = sizeof(ifType); + dwValueType = REG_DWORD; + res = RegQueryValueExW(hKey, L"*IfType", NULL, + &dwValueType, (LPBYTE)&ifType, &cbSize); + if (res != 0) + return HRESULT_FROM_WIN32(res); + + ZeroMemory(pLUID, sizeof(NET_LUID)); + pLUID->Info.IfType = ifType; + pLUID->Info.NetLuidIndex = luidIndex; + + return res; +} + + +HRESULT vboxNetCfgWinSetupMetric(IN NET_LUID* pLuid) +{ + HINSTANCE hModule = NULL; + HRESULT rc = vboxLoadIpHelpFunctions(hModule); + if (SUCCEEDED(rc)) + { + int loopbackMetric; + rc = vboxNetCfgWinGetLoopbackMetric(&loopbackMetric); + if (SUCCEEDED(rc)) + rc = vboxNetCfgWinSetInterfaceMetric(pLuid, loopbackMetric - 1); + } + + g_pfnInitializeIpInterfaceEntry = NULL; + g_pfnSetIpInterfaceEntry = NULL; + g_pfnGetIpInterfaceEntry = NULL; + + FreeLibrary(hModule); + return rc; +} +#ifdef VBOXNETCFG_DELAYEDRENAME +VBOXNETCFGWIN_DECL(HRESULT) VBoxNetCfgWinRenameHostOnlyConnection(IN const GUID *pGuid, IN LPCWSTR pwszId, OUT BSTR *pDevName) +{ + HRESULT hr = S_OK; + WCHAR wszDevName[256]; + WCHAR wszConnectionNewName[128]; + ULONG cbName = sizeof(wszConnectionNewName); + + HDEVINFO hDevInfo = SetupDiCreateDeviceInfoList(&GUID_DEVCLASS_NET, NULL); + if (hDevInfo != INVALID_HANDLE_VALUE) + { + SP_DEVINFO_DATA DevInfoData; + + DevInfoData.cbSize = sizeof(SP_DEVINFO_DATA); + if (SetupDiOpenDeviceInfo(hDevInfo, pwszId, NULL, 0, &DevInfoData)) + { + DWORD err = ERROR_SUCCESS; + if (!SetupDiGetDeviceRegistryPropertyW(hDevInfo, &DevInfoData, + SPDRP_FRIENDLYNAME, NULL, + (PBYTE)wszDevName, RT_ELEMENTS(wszDevName), NULL)) + { + err = GetLastError(); + if (err == ERROR_INVALID_DATA) + { + err = SetupDiGetDeviceRegistryPropertyW(hDevInfo, &DevInfoData, + SPDRP_DEVICEDESC, NULL, + (PBYTE)wszDevName, RT_ELEMENTS(wszDevName), NULL) + ? ERROR_SUCCESS + : GetLastError(); + } + } + if (err == ERROR_SUCCESS) + { + hr = VBoxNetCfgWinGenHostonlyConnectionName(wszDevName, wszConnectionNewName, &cbName); + if (SUCCEEDED(hr)) + { + WCHAR wszGuid[50]; + int cbWGuid = StringFromGUID2(*pGuid, wszGuid, RT_ELEMENTS(wszGuid)); + if (cbWGuid) + { + hr = VBoxNetCfgWinRenameConnection(wszGuid, wszConnectionNewName); + if (FAILED(hr)) + NonStandardLogFlow(("VBoxNetCfgWinRenameHostOnlyConnection: VBoxNetCfgWinRenameConnection failed (0x%x)\n", hr)); + } + else + { + err = GetLastError(); + hr = HRESULT_FROM_WIN32(err); + if (SUCCEEDED(hr)) + hr = E_FAIL; + NonStandardLogFlow(("StringFromGUID2 failed err=%u, hr=0x%x\n", err, hr)); + } + } + else + NonStandardLogFlow(("VBoxNetCfgWinRenameHostOnlyConnection: VBoxNetCfgWinGenHostonlyConnectionName failed (0x%x)\n", hr)); + if (SUCCEEDED(hr) && pDevName) + { + *pDevName = SysAllocString((const OLECHAR *)wszDevName); + if (!*pDevName) + { + NonStandardLogFlow(("SysAllocString failed\n")); + hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY); + } + } + } + else + { + hr = HRESULT_FROM_WIN32(err); + NonStandardLogFlow(("VBoxNetCfgWinRenameHostOnlyConnection: SetupDiGetDeviceRegistryPropertyW failed (0x%x)\n", err)); + } + } + else + { + DWORD err = GetLastError(); + hr = HRESULT_FROM_WIN32(err); + NonStandardLogFlow(("VBoxNetCfgWinRenameHostOnlyConnection: SetupDiOpenDeviceInfo failed (0x%x)\n", err)); + } + SetupDiDestroyDeviceInfoList(hDevInfo); + } + + return hr; +} +#endif /* VBOXNETCFG_DELAYEDRENAME */ + +#undef SetErrBreak + diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/drv/Makefile.kup b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/Makefile.kup new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/Makefile.kup diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetAdp.inf b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetAdp.inf new file mode 100644 index 00000000..812d622e --- /dev/null +++ b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetAdp.inf @@ -0,0 +1,94 @@ +; $Id: VBoxNetAdp.inf $ +;; @file +; VBoxNetAdp.inf - VirtualBox Host-Only Driver inf file +; + +; +; Copyright (C) 2011-2019 Oracle Corporation +; +; This file is part of VirtualBox Open Source Edition (OSE), as +; available from http://www.virtualbox.org. This file is free software; +; you can redistribute it and/or modify it under the terms of the GNU +; 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. +; +; The contents of this file may alternatively be used under the terms +; of the Common Development and Distribution License Version 1.0 +; (CDDL) only, as it comes in the "COPYING.CDDL" file of the +; VirtualBox OSE 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. +; + +[Version] +signature = "$Windows NT$" +;cat CatalogFile = VBoxNetAdp.cat +Class = Net +ClassGUID = {4d36e972-e325-11ce-bfc1-08002be10318} +Provider = %Provider% +; DriverPackageType=Network +; DriverPackageDisplayName=%VBoxNetAdp_Desc% +;edit-DriverVer=08/13/2008,1.1.0.1 + +[ControlFlags] +;ExcludeFromSelect = sun_VBoxNetAdp + +[SourceDisksNames] +1=%DiskDescription%,"",, + +[SourceDisksFiles] +VBoxNetAdp.sys=1 + +[DestinationDirs] +DefaultDestDir = 12 +VBoxNetAdp.Files.Sys = 12 ; %windir%\System32\drivers + +[Manufacturer] +%Provider% = VBox,NTx86,NTia64,NTamd64 + +[VBox] +%VBoxNetAdp_Desc% = VBoxNetAdp.ndi, sun_VBoxNetAdp + +[VBox.NTx86] +%VBoxNetAdp_Desc% = VBoxNetAdp.ndi, sun_VBoxNetAdp + +[VBox.NTia64] +%VBoxNetAdp_Desc% = VBoxNetAdp.ndi, sun_VBoxNetAdp + +[VBox.NTamd64] +%VBoxNetAdp_Desc% = VBoxNetAdp.ndi, sun_VBoxNetAdp + +[VBoxNetAdp.ndi] +Characteristics = 0x1 ; NCF_VIRTUAL +CopyFiles = VBoxNetAdp.Files.Sys +AddReg = VBoxNetAdp.AddReg + +[VBoxNetAdp.Files.Sys] +VBoxNetAdp.sys,,,2 + +[VBoxNetAdp.ndi.Services] +AddService = VBoxNetAdp,0x2, VBoxNetAdp.AddService + +[VBoxNetAdp.AddService] +DisplayName = %VBoxNetAdp_Desc% +ServiceType = 1 ;SERVICE_KERNEL_DRIVER +StartType = 3 ;SERVICE_DEMAND_START +ErrorControl = 1 ;SERVICE_ERROR_NORMAL +ServiceBinary = %12%\VBoxNetAdp.sys +LoadOrderGroup = NDIS + +[VBoxNetAdp.AddReg] +HKR, , *NdisDeviceType, 0x00010001, 1 ; NDIS_DEVICE_TYPE_ENDPOINT +HKR, Ndi, Service, 0, "VBoxNetAdp" +HKR, Ndi\Interfaces, UpperRange, 0, "ndis5" +HKR, Ndi\Interfaces, LowerRange, 0, "ethernet" + +[Strings] +Provider = "Oracle Corporation" +VBoxNetAdp_Desc = "VirtualBox Host-Only Ethernet Adapter" +DiskDescription = "VirtualBox Host-Only Ethernet Adapter" + diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFlt-win.rc b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFlt-win.rc new file mode 100644 index 00000000..4ba92008 --- /dev/null +++ b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFlt-win.rc @@ -0,0 +1,67 @@ +/* $Id: VBoxNetFlt-win.rc $ */ +/** @file + * VBoxNetFlt - Resource file containing version info and icon. + */ +/* + * Copyright (C) 2011-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * 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. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE 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. + */ + +#include <windows.h> +#include <VBox/version.h> + +#ifndef VBOXNETADP +# define DESCRIPTION_STR "VirtualBox Bridged Networking Driver\0" +# define FILENAME_STR "VBoxNetFlt" +#else +# define DESCRIPTION_STR "VirtualBox Host-Only Network Adapter Driver\0" +# define FILENAME_STR "VBoxNetAdp" +#endif + +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VBOX_RC_FILE_VERSION + PRODUCTVERSION VBOX_RC_FILE_VERSION + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK + FILEFLAGS VBOX_RC_FILE_FLAGS + FILEOS VBOX_RC_FILE_OS + FILETYPE VBOX_RC_TYPE_DRV + FILESUBTYPE VFT2_DRV_NETWORK +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" // Lang=US English, CharSet=Unicode + BEGIN + VALUE "CompanyName", VBOX_RC_COMPANY_NAME + VALUE "FileDescription", DESCRIPTION_STR + VALUE "FileVersion", VBOX_RC_FILE_VERSION_STR + VALUE "InternalName", FILENAME_STR "\0" + VALUE "LegalCopyright", VBOX_RC_LEGAL_COPYRIGHT + VALUE "OriginalFilename", FILENAME_STR ".sys\0" + VALUE "ProductName", VBOX_RC_PRODUCT_NAME_STR + VALUE "ProductVersion", VBOX_RC_PRODUCT_VERSION_STR + VBOX_RC_MORE_STRINGS + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFlt.inf b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFlt.inf new file mode 100644 index 00000000..90a80988 --- /dev/null +++ b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFlt.inf @@ -0,0 +1,113 @@ +; $Id: VBoxNetFlt.inf $ +;; @file +; VBoxNetFlt.inf - VirtualBox Bridged Networking Driver inf file Protocol edge +; + +; +; Copyright (C) 2011-2019 Oracle Corporation +; +; This file is part of VirtualBox Open Source Edition (OSE), as +; available from http://www.virtualbox.org. This file is free software; +; you can redistribute it and/or modify it under the terms of the GNU +; 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. +; +; The contents of this file may alternatively be used under the terms +; of the Common Development and Distribution License Version 1.0 +; (CDDL) only, as it comes in the "COPYING.CDDL" file of the +; VirtualBox OSE 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. +; + +[Version] +Signature = "$Windows NT$" +;cat CatalogFile = VBoxNetFlt.cat +Class = NetService +ClassGUID = {4D36E974-E325-11CE-BFC1-08002BE10318} +Provider = %Provider% +;DriverPackageType=Network +;DriverPackageDisplayName=%VBoxNetFlt_Desc% +;edit-DriverVer=08/13/2008,1.1.0.1 + + +[Manufacturer] +%Provider% = VBox,NTx86,NTamd64 + +[ControlFlags] + +[VBox] +%VBoxNetFlt_Desc% = VBoxNetFlt.ndi, sun_VBoxNetFlt + +[VBox.NTx86] +%VBoxNetFlt_Desc% = VBoxNetFlt.ndi, sun_VBoxNetFlt + +[VBox.NTamd64] +%VBoxNetFlt_Desc% = VBoxNetFlt.ndi, sun_VBoxNetFlt + +[VBoxNetFlt.ndi] +AddReg = VBoxNetFlt.ndi.AddReg, VBoxNetFlt.AddReg +Characteristics = 0x4410 ; NCF_FILTER | NCF_NDIS_PROTOCOL +CopyFiles = VBoxNetFlt.Files.DLL, VBoxNetFlt.Files.Sys +CopyInf = VBoxNetFltM.inf + +[VBoxNetFlt.ndi.Remove] +DelFiles = VBoxNetFlt.Files.DLL, VBoxNetFlt.Files.Sys + +[VBoxNetFlt.ndi.Services] +AddService = VBoxNetFlt,, VBoxNetFlt.AddService + +[VBoxNetFlt.AddService] +DisplayName = %VBoxNetFltService_Desc% +ServiceType = 1 ;SERVICE_KERNEL_DRIVER +StartType = 3 ;SERVICE_DEMAND_START +ErrorControl = 1 ;SERVICE_ERROR_NORMAL +ServiceBinary = %12%\VBoxNetFlt.sys +LoadOrderGroup = PNP_TDI +AddReg = VBoxNetFlt.AddService.AddReg + + +[VBoxNetFlt.AddService.AddReg] + +[SourceDisksNames] +1=%DiskDescription%,"",, + +[SourceDisksFiles] +VBoxNetFlt.sys=1 +VBoxNetFltNobj.dll=1 + +[DestinationDirs] +DefaultDestDir = 12 +VBoxNetFlt.Files.DLL = 11 ; %windir%\System32 +VBoxNetFlt.Files.Sys = 12 ; %windir%\System32\drivers + +[VBoxNetFlt.Files.Sys] +VBoxNetFlt.sys,,,2 + +[VBoxNetFlt.Files.DLL] +VBoxNetFltNobj.dll,,,2 + +[VBoxNetFlt.ndi.AddReg] +HKR, Ndi, HelpText, , %VBoxNetFlt_HELP% +HKR, Ndi, ClsID, 0, {f374d1a0-bf08-4bdc-9cb2-c15ddaeef955} +HKR, Ndi, ComponentDll, , VBoxNetFltNobj.dll +HKR, Ndi, FilterClass, , failover +HKR, Ndi, FilterDeviceInfId, , sun_VBoxNetFltmp +HKR, Ndi, Service, , VBoxNetFlt +HKR, Ndi\Interfaces, UpperRange, , noupper +HKR, Ndi\Interfaces, LowerRange, , nolower +HKR, Ndi\Interfaces, FilterMediaTypes, , "ethernet, nolower" + +[VBoxNetFlt.AddReg] +HKR, Parameters, Param1, 0, 4 + +[Strings] +Provider = "Oracle Corporation" +DiskDescription = "VirtualBox Bridged Networking Driver" +VBoxNetFlt_Desc = "VirtualBox Bridged Networking Driver" +VBoxNetFlt_HELP = "VirtualBox Bridged Networking Driver" +VBoxNetFltService_Desc = "VirtualBox Bridged Networking Service" diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltCmn-win.h b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltCmn-win.h new file mode 100644 index 00000000..9746ce33 --- /dev/null +++ b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltCmn-win.h @@ -0,0 +1,523 @@ +/* $Id: VBoxNetFltCmn-win.h $ */ +/** @file + * VBoxNetFltCmn-win.h - Bridged Networking Driver, Windows Specific Code. + * Common header with configuration defines and global defs + */ + +/* + * Copyright (C) 2011-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * 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. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE 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. + */ + +#ifndef VBOX_INCLUDED_SRC_VBoxNetFlt_win_drv_VBoxNetFltCmn_win_h +#define VBOX_INCLUDED_SRC_VBoxNetFlt_win_drv_VBoxNetFltCmn_win_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#define LOG_GROUP LOG_GROUP_NET_FLT_DRV + +/* debugging flags */ +#ifdef DEBUG +//# define DEBUG_NETFLT_PACKETS +# ifndef DEBUG_misha +# define RT_NO_STRICT +# endif +/* # define DEBUG_NETFLT_LOOPBACK */ +/* receive logic has several branches */ +/* the DEBUG_NETFLT_RECV* macros used to debug the ProtocolReceive callback + * which is typically not used in case the underlying miniport indicates the packets with NdisMIndicateReceivePacket + * the best way to debug the ProtocolReceive (which in turn has several branches) is to enable the DEBUG_NETFLT_RECV + * one by one in the below order, i.e. + * first DEBUG_NETFLT_RECV + * then DEBUG_NETFLT_RECV + DEBUG_NETFLT_RECV_NOPACKET */ +//# define DEBUG_NETFLT_RECV +//# define DEBUG_NETFLT_RECV_NOPACKET +//# define DEBUG_NETFLT_RECV_TRANSFERDATA +/* use ExAllocatePoolWithTag instead of NdisAllocateMemoryWithTag */ +// #define DEBUG_NETFLT_USE_EXALLOC +#endif + +#include <VBox/intnet.h> +#include <VBox/log.h> +#include <VBox/err.h> +#include <VBox/version.h> +#include <iprt/initterm.h> +#include <iprt/assert.h> +#include <iprt/spinlock.h> +#include <iprt/semaphore.h> +#include <iprt/process.h> +#include <iprt/alloc.h> +#include <iprt/alloca.h> +#include <iprt/time.h> +#include <iprt/net.h> +#include <iprt/list.h> + +#include <iprt/nt/ntddk.h> +#include <iprt/nt/ndis.h> + +#define VBOXNETFLT_OS_SPECFIC 1 + +/** version + * NOTE: we are NOT using NDIS 5.1 features now */ +#ifdef NDIS51_MINIPORT +# define VBOXNETFLT_VERSION_MP_NDIS_MAJOR 5 +# define VBOXNETFLT_VERSION_MP_NDIS_MINOR 1 +#else +# define VBOXNETFLT_VERSION_MP_NDIS_MAJOR 5 +# define VBOXNETFLT_VERSION_MP_NDIS_MINOR 0 +#endif + +#ifndef VBOXNETADP +#ifdef NDIS51 +# define VBOXNETFLT_VERSION_PT_NDIS_MAJOR 5 +# define VBOXNETFLT_VERSION_PT_NDIS_MINOR 1 /* todo: use 0 here as well ? */ +#else +# define VBOXNETFLT_VERSION_PT_NDIS_MAJOR 5 +# define VBOXNETFLT_VERSION_PT_NDIS_MINOR 0 +#endif + +# define VBOXNETFLT_NAME_PROTOCOL L"VBoxNetFlt" +/** device to be used to prevent the driver unload & ioctl interface (if necessary in the future) */ +# define VBOXNETFLT_NAME_LINK L"\\DosDevices\\Global\\VBoxNetFlt" +# define VBOXNETFLT_NAME_DEVICE L"\\Device\\VBoxNetFlt" +#else +# define VBOXNETFLT_NAME_LINK L"\\DosDevices\\Global\\VBoxNetAdp" +# define VBOXNETFLT_NAME_DEVICE L"\\Device\\VBoxNetAdp" +#endif + +typedef struct VBOXNETFLTINS *PVBOXNETFLTINS; + +/** configuration */ + +/** Ndis Packet pool settings + * these are applied to both receive and send packet pools */ +/* number of packets for normal used */ +#define VBOXNETFLT_PACKET_POOL_SIZE_NORMAL 0x000000FF +/* number of additional overflow packets */ +#define VBOXNETFLT_PACKET_POOL_SIZE_OVERFLOW 0x0000FF00 + +/** packet queue size used when the driver is working in the "active" mode */ +#define VBOXNETFLT_PACKET_INFO_POOL_SIZE 0x0000FFFF + +#ifndef VBOXNETADP +/** memory tag used for memory allocations + * (VBNF stands for VBox NetFlt) */ +# define VBOXNETFLT_MEM_TAG 'FNBV' +#else +/** memory tag used for memory allocations + * (VBNA stands for VBox NetAdp) */ +# define VBOXNETFLT_MEM_TAG 'ANBV' +#endif + +/** receive and transmit Ndis buffer pool size */ +#define VBOXNETFLT_BUFFER_POOL_SIZE_TX 128 +#define VBOXNETFLT_BUFFER_POOL_SIZE_RX 128 + +#define VBOXNETFLT_PACKET_ETHEADER_SIZE 14 +#define VBOXNETFLT_PACKET_HEADER_MATCH_SIZE 24 +#define VBOXNETFLT_PACKET_QUEUE_SG_SEGS_ALLOC 32 + + +#if defined(DEBUG_NETFLT_PACKETS) || !defined(VBOX_LOOPBACK_USEFLAGS) +# define VBOXNETFLT_PACKETMATCH_LENGTH (VBOXNETFLT_PACKET_ETHEADER_SIZE + 2) +#endif + +#ifdef VBOXNETADP +#define VBOXNETADP_HEADER_SIZE 14 +#define VBOXNETADP_MAX_DATA_SIZE 1500 +#define VBOXNETADP_MAX_PACKET_SIZE (VBOXNETADP_HEADER_SIZE + VBOXNETADP_MAX_DATA_SIZE) +#define VBOXNETADP_MIN_PACKET_SIZE 60 +/* link speed 100Mbps (measured in 100 bps) */ +#define VBOXNETADP_LINK_SPEED 1000000 +#define VBOXNETADP_MAX_LOOKAHEAD_SIZE VBOXNETADP_MAX_DATA_SIZE +#define VBOXNETADP_VENDOR_ID 0x080027 +#define VBOXNETADP_VENDOR_DRIVER_VERSION 0x00010000 +#define VBOXNETADP_VENDOR_DESC "Sun" +#define VBOXNETADP_MAX_MCAST_LIST 32 +#define VBOXNETADP_ETH_ADDRESS_LENGTH 6 + +//#define VBOXNETADP_REPORT_DISCONNECTED +#endif +/* type defs */ + +/** Flag specifying that the type of enqueued packet + * if set the info contains the PINTNETSG packet + * if clear the packet info contains the PNDIS_PACKET packet + * Typically the packet queue we are maintaining contains PNDIS_PACKETs only, + * however in case the underlying miniport indicates a packet with the NDIS_STATUS_RESOURCES status + * we MUST return the packet back to the miniport immediately + * this is why we are creating the INTNETSG, copying the ndis packet info there and enqueueing it */ +#define VBOXNETFLT_PACKET_SG 0x00000001 + +/** the flag specifying that the packet source + * if set the packet comes from the host (upperlying protocol) + * if clear the packet comes from the wire (underlying miniport) */ +#define VBOXNETFLT_PACKET_SRC_HOST 0x00000002 + +#ifndef VBOXNETFLT_NO_PACKET_QUEUE +/** flag specifying the packet was originated by our driver + * i.e. we could use it on our needs and should not return it + * we are enqueueing "our" packets on ProtocolReceive call-back when + * Ndis does not give us a receive packet (the driver below us has called NdisM..IndicateReceive) + * this is supported for Ndis Packet only */ +#define VBOXNETFLT_PACKET_MINE 0x00000004 + +/** flag passed to vboxNetFltWinQuEnqueuePacket specifying that the packet should be copied + * this is supported for Ndis Packet only */ +#define VBOXNETFLT_PACKET_COPY 0x00000008 +#endif + +/** packet queue element containing the packet info */ +typedef struct VBOXNETFLT_PACKET_INFO +{ + /** list entry used for enqueueing the info */ + LIST_ENTRY ListEntry; + /** pointer to the pool containing this packet info */ + struct VBOXNETFLT_PACKET_INFO_POOL *pPool; + /** flags describing the referenced packet. Contains PACKET_xxx flags (i.e. PACKET_SG, PACKET_SRC_HOST) */ + uint32_t fFlags; + /** pointer to the packet this info represents */ + PVOID pPacket; +} VBOXNETFLT_PACKET_INFO, *PVBOXNETFLT_PACKET_INFO; + +/* paranoid check to make sure the elements in the packet info array are properly aligned */ +AssertCompile((sizeof(VBOXNETFLT_PACKET_INFO) & (sizeof(PVOID) - 1)) == 0); + +/** represents the packet queue */ +typedef LIST_ENTRY PVBOXNETFLT_ACKET_QUEUE, *PVBOXNETFLT_PACKET_QUEUE; + +/* + * we are using non-interlocked versions of LIST_ENTRY-related operations macros and synchronize + * access to the queue and its elements by acquiring/releasing a spinlock using Ndis[Acquire,Release]Spinlock + * + * we are NOT using interlocked versions of insert/remove head/tail list functions because we need to iterate though + * the queue elements as well as remove elements from the midle of the queue + * + * * @todo: it seems that we can switch to using interlocked versions of list-entry functions + * since we have removed all functionality (mentioned above, i.e. queue elements iteration, etc.) that might prevent us from doing this + */ +typedef struct VBOXNETFLT_INTERLOCKED_PACKET_QUEUE +{ + /** queue */ + PVBOXNETFLT_ACKET_QUEUE Queue; + /** queue lock */ + NDIS_SPIN_LOCK Lock; +} VBOXNETFLT_INTERLOCKED_PACKET_QUEUE, *PVBOXNETFLT_INTERLOCKED_PACKET_QUEUE; + +typedef struct VBOXNETFLT_SINGLE_LIST +{ + /** queue */ + SINGLE_LIST_ENTRY Head; + /** pointer to the list tail. used to enqueue elements to the tail of the list */ + PSINGLE_LIST_ENTRY pTail; +} VBOXNETFLT_SINGLE_LIST, *PVBOXNETFLT_SINGLE_LIST; + +typedef struct VBOXNETFLT_INTERLOCKED_SINGLE_LIST +{ + /** queue */ + VBOXNETFLT_SINGLE_LIST List; + /** queue lock */ + NDIS_SPIN_LOCK Lock; +} VBOXNETFLT_INTERLOCKED_SINGLE_LIST, *PVBOXNETFLT_INTERLOCKED_SINGLE_LIST; + +/** packet info pool contains free packet info elements to be used for the packet queue + * we are using the pool mechanism to allocate packet queue elements + * the pool mechanism is pretty simple now, we are allocating a bunch of memory + * for maintaining VBOXNETFLT_PACKET_INFO_POOL_SIZE queue elements and just returning null when the pool is exhausted + * This mechanism seems to be enough for now since we are using VBOXNETFLT_PACKET_INFO_POOL_SIZE = 0xffff which is + * the maximum size of packets the ndis packet pool supports */ +typedef struct VBOXNETFLT_PACKET_INFO_POOL +{ + /** free packet info queue */ + VBOXNETFLT_INTERLOCKED_PACKET_QUEUE Queue; + /** memory bugger used by the pool */ + PVOID pBuffer; +} VBOXNETFLT_PACKET_INFO_POOL, *PVBOXNETFLT_PACKET_INFO_POOL; + +typedef enum VBOXNETDEVOPSTATE +{ + kVBoxNetDevOpState_InvalidValue = 0, + kVBoxNetDevOpState_Initializing, + kVBoxNetDevOpState_Initialized, + kVBoxNetDevOpState_Deinitializing, + kVBoxNetDevOpState_Deinitialized, + +} VBOXNETDEVOPSTATE; + +typedef enum VBOXNETFLT_WINIFSTATE +{ + /** The usual invalid state. */ + kVBoxWinIfState_Invalid = 0, + /** Initialization. */ + kVBoxWinIfState_Connecting, + /** Connected fuly functional state */ + kVBoxWinIfState_Connected, + /** Disconnecting */ + kVBoxWinIfState_Disconnecting, + /** Disconnected */ + kVBoxWinIfState_Disconnected, +} VBOXNETFLT_WINIFSTATE; + +/** structure used to maintain the state and reference count of the miniport and protocol */ +typedef struct VBOXNETFLT_WINIF_DEVICE +{ + /** initialize state */ + VBOXNETDEVOPSTATE OpState; + /** ndis power state */ + NDIS_DEVICE_POWER_STATE PowerState; + /** reference count */ + uint32_t cReferences; +} VBOXNETFLT_WINIF_DEVICE, *PVBOXNETFLT_WINIF_DEVICE; + +#define VBOXNDISREQUEST_INPROGRESS 1 +#define VBOXNDISREQUEST_QUEUED 2 + +typedef struct VBOXNETFLTWIN_STATE +{ + union + { + struct + { + UINT fRequestInfo : 2; + UINT fInterfaceClosing : 1; + UINT fStandBy : 1; + UINT fProcessingPacketFilter : 1; + UINT fPPFNetFlt : 1; + UINT fUpperProtSetFilterInitialized : 1; + UINT Reserved : 25; + }; + UINT Value; + }; +} VBOXNETFLTWIN_STATE, *PVBOXNETFLTWIN_STATE; + +DECLINLINE(VBOXNETFLTWIN_STATE) vboxNetFltWinAtomicUoReadWinState(VBOXNETFLTWIN_STATE State) +{ + UINT fValue = ASMAtomicUoReadU32((volatile uint32_t *)&State.Value); + return *((PVBOXNETFLTWIN_STATE)((void*)&fValue)); +} + +/* miniport layer globals */ +typedef struct VBOXNETFLTGLOBALS_MP +{ + /** our miniport handle */ + NDIS_HANDLE hMiniport; + /** ddis wrapper handle */ + NDIS_HANDLE hNdisWrapper; +} VBOXNETFLTGLOBALS_MP, *PVBOXNETFLTGLOBALS_MP; + +#ifndef VBOXNETADP +/* protocol layer globals */ +typedef struct VBOXNETFLTGLOBALS_PT +{ + /** our protocol handle */ + NDIS_HANDLE hProtocol; +} VBOXNETFLTGLOBALS_PT, *PVBOXNETFLTGLOBALS_PT; +#endif /* #ifndef VBOXNETADP */ + +typedef struct VBOXNETFLTGLOBALS_WIN +{ + /** synch event used for device creation synchronization */ + KEVENT SynchEvent; + /** Device reference count */ + int cDeviceRefs; + /** ndis device */ + NDIS_HANDLE hDevice; + /** device object */ + PDEVICE_OBJECT pDevObj; + /* loopback flags */ + /* ndis packet flags to disable packet loopback */ + UINT fPacketDontLoopBack; + /* ndis packet flags specifying whether the packet is looped back */ + UINT fPacketIsLoopedBack; + /* Minport info */ + VBOXNETFLTGLOBALS_MP Mp; +#ifndef VBOXNETADP + /* Protocol info */ + VBOXNETFLTGLOBALS_PT Pt; + /** lock protecting the filter list */ + NDIS_SPIN_LOCK lockFilters; + /** the head of filter list */ + RTLISTANCHOR listFilters; + /** IP address change notifier handle */ + HANDLE hNotifier; +#endif +} VBOXNETFLTGLOBALS_WIN, *PVBOXNETFLTGLOBALS_WIN; + +extern VBOXNETFLTGLOBALS_WIN g_VBoxNetFltGlobalsWin; + +/** represents filter driver device context*/ +typedef struct VBOXNETFLTWIN +{ + /** handle used by miniport edge for ndis calls */ + NDIS_HANDLE hMiniport; + /** miniport edge state */ + VBOXNETFLT_WINIF_DEVICE MpState; + /** ndis packet pool used for receives */ + NDIS_HANDLE hRecvPacketPool; + /** ndis buffer pool used for receives */ + NDIS_HANDLE hRecvBufferPool; + /** driver bind adapter state. */ + VBOXNETFLT_WINIFSTATE enmState; +#ifndef VBOXNETADP + /* misc state flags */ + VBOXNETFLTWIN_STATE StateFlags; + /** handle used by protocol edge for ndis calls */ + NDIS_HANDLE hBinding; + /** protocol edge state */ + VBOXNETFLT_WINIF_DEVICE PtState; + /** ndis packet pool used for receives */ + NDIS_HANDLE hSendPacketPool; + /** ndis buffer pool used for receives */ + NDIS_HANDLE hSendBufferPool; + /** used for maintaining the pending send packets for handling packet loopback */ + VBOXNETFLT_INTERLOCKED_SINGLE_LIST SendPacketQueue; + /** used for serializing calls to the NdisRequest in the vboxNetFltWinSynchNdisRequest */ + RTSEMFASTMUTEX hSynchRequestMutex; + /** event used to synchronize with the Ndis Request completion in the vboxNetFltWinSynchNdisRequest */ + KEVENT hSynchCompletionEvent; + /** status of the Ndis Request initiated by the vboxNetFltWinSynchNdisRequest */ + NDIS_STATUS volatile SynchCompletionStatus; + /** pointer to the Ndis Request being executed by the vboxNetFltWinSynchNdisRequest */ + PNDIS_REQUEST volatile pSynchRequest; + /** open/close adapter status. + * Since ndis adapter open and close requests may complete asynchronously, + * we are using event mechanism to wait for open/close completion + * the status field is being set by the completion call-back */ + NDIS_STATUS OpenCloseStatus; + /** open/close adaptor completion event */ + NDIS_EVENT OpenCloseEvent; + /** medium we are attached to */ + NDIS_MEDIUM enmMedium; + /** + * Passdown request info + */ + /** ndis request we pass down to the miniport below */ + NDIS_REQUEST PassDownRequest; + /** Ndis pass down request bytes read or written original pointer */ + PULONG pcPDRBytesRW; + /** Ndis pass down request bytes needed original pointer */ + PULONG pcPDRBytesNeeded; + /** true if we should indicate the receive complete used by the ProtocolReceive mechanism. + * We need to indicate it only with the ProtocolReceive + NdisMEthIndicateReceive path. + * Note: we're using KeGetCurrentProcessorNumber, which is not entirely correct in case + * we're running on 64bit win7+, which can handle > 64 CPUs, however since KeGetCurrentProcessorNumber + * always returns the number < than the number of CPUs in the first group, we're guaranteed to have CPU index < 64 + * @todo: use KeGetCurrentProcessorNumberEx for Win7+ 64 and dynamically extended array */ + bool abIndicateRxComplete[64]; + /** Pending transfer data packet queue (i.e. packets that were indicated as pending on NdisTransferData call */ + VBOXNETFLT_INTERLOCKED_SINGLE_LIST TransferDataList; + /* mac options initialized on OID_GEN_MAC_OPTIONS */ + ULONG fMacOptions; + /** our miniport devuice name */ + NDIS_STRING MpDeviceName; + /** synchronize with unbind with Miniport initialization */ + NDIS_EVENT MpInitCompleteEvent; + /** media connect status that we indicated */ + NDIS_STATUS MpIndicatedMediaStatus; + /** media connect status pending to indicate */ + NDIS_STATUS MpUnindicatedMediaStatus; + /** packet filter flags set by the upper protocols */ + ULONG fUpperProtocolSetFilter; + /** packet filter flags set by the upper protocols */ + ULONG fSetFilterBuffer; + /** packet filter flags set by us */ + ULONG fOurSetFilter; + /** our own list of filters, needed by notifier */ + RTLISTNODE node; +#else + volatile ULONG cTxSuccess; + volatile ULONG cRxSuccess; + volatile ULONG cTxError; + volatile ULONG cRxError; +#endif +} VBOXNETFLTWIN, *PVBOXNETFLTWIN; + +typedef struct VBOXNETFLT_PACKET_QUEUE_WORKER +{ + /** this event is used to initiate a packet queue worker thread kill */ + KEVENT KillEvent; + /** this event is used to notify a worker thread that the packets are added to the queue */ + KEVENT NotifyEvent; + /** pointer to the packet queue worker thread object */ + PKTHREAD pThread; + /** pointer to the SG used by the packet queue for IntNet receive notifications */ + PINTNETSG pSG; + /** Packet queue */ + VBOXNETFLT_INTERLOCKED_PACKET_QUEUE PacketQueue; + /** Packet info pool, i.e. the pool for the packet queue elements */ + VBOXNETFLT_PACKET_INFO_POOL PacketInfoPool; +} VBOXNETFLT_PACKET_QUEUE_WORKER, *PVBOXNETFLT_PACKET_QUEUE_WORKER; + +/* protocol reserved data held in ndis packet */ +typedef struct VBOXNETFLT_PKTRSVD_PT +{ + /** original packet received from the upperlying protocol + * can be null if the packet was originated by intnet */ + PNDIS_PACKET pOrigPacket; + /** pointer to the buffer to be freed on send completion + * can be null if no buffer is to be freed */ + PVOID pBufToFree; +#if !defined(VBOX_LOOPBACK_USEFLAGS) || defined(DEBUG_NETFLT_PACKETS) + SINGLE_LIST_ENTRY ListEntry; + /* true if the packet is from IntNet */ + bool bFromIntNet; +#endif +} VBOXNETFLT_PKTRSVD_PT, *PVBOXNETFLT_PKTRSVD_PT; + +/** miniport reserved data held in ndis packet */ +typedef struct VBOXNETFLT_PKTRSVD_MP +{ + /** original packet received from the underling miniport + * can be null if the packet was originated by intnet */ + PNDIS_PACKET pOrigPacket; + /** pointer to the buffer to be freed on receive completion + * can be null if no buffer is to be freed */ + PVOID pBufToFree; +} VBOXNETFLT_PKTRSVD_MP, *PVBOXNETFLT_PKTRSVD_MP; + +/** represents the data stored in the protocol reserved field of ndis packet on NdisTransferData processing */ +typedef struct VBOXNETFLT_PKTRSVD_TRANSFERDATA_PT +{ + /** next packet in a list */ + SINGLE_LIST_ENTRY ListEntry; + /* packet buffer start */ + PNDIS_BUFFER pOrigBuffer; +} VBOXNETFLT_PKTRSVD_TRANSFERDATA_PT, *PVBOXNETFLT_PKTRSVD_TRANSFERDATA_PT; + +/* VBOXNETFLT_PKTRSVD_TRANSFERDATA_PT should fit into PROTOCOL_RESERVED_SIZE_IN_PACKET because we use protocol reserved part + * of our miniport edge on transfer data processing for honding our own info */ +AssertCompile(sizeof (VBOXNETFLT_PKTRSVD_TRANSFERDATA_PT) <= PROTOCOL_RESERVED_SIZE_IN_PACKET); +/* this should fit in MiniportReserved */ +AssertCompile(sizeof (VBOXNETFLT_PKTRSVD_MP) <= RT_SIZEOFMEMB(NDIS_PACKET, MiniportReserved)); +/* we use RTAsmAtomic*U32 for those, make sure we're correct */ +AssertCompile(sizeof (NDIS_DEVICE_POWER_STATE) == sizeof (uint32_t)); +AssertCompile(sizeof (UINT) == sizeof (uint32_t)); + + +#define NDIS_FLAGS_SKIP_LOOPBACK_W2K 0x400 + +#include "../../VBoxNetFltInternal.h" +#include "VBoxNetFltRt-win.h" +#ifndef VBOXNETADP +# include "VBoxNetFltP-win.h" +#endif +#include "VBoxNetFltM-win.h" + +#endif /* !VBOX_INCLUDED_SRC_VBoxNetFlt_win_drv_VBoxNetFltCmn_win_h */ diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltM-win.cpp b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltM-win.cpp new file mode 100644 index 00000000..0b5971b0 --- /dev/null +++ b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltM-win.cpp @@ -0,0 +1,1560 @@ +/* $Id: VBoxNetFltM-win.cpp $ */ +/** @file + * VBoxNetFltM-win.cpp - Bridged Networking Driver, Windows Specific Code. + * Miniport edge + */ +/* + * Copyright (C) 2011-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * 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. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE 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. + */ +#include "VBoxNetFltCmn-win.h" + +static const char* vboxNetFltWinMpDumpOid(ULONG oid); + +#ifndef VBOXNETADP +static NDIS_STATUS vboxNetFltWinMpInitialize(OUT PNDIS_STATUS OpenErrorStatus, + OUT PUINT SelectedMediumIndex, + IN PNDIS_MEDIUM MediumArray, + IN UINT MediumArraySize, + IN NDIS_HANDLE MiniportAdapterHandle, + IN NDIS_HANDLE WrapperConfigurationContext) +{ + RT_NOREF1(WrapperConfigurationContext); + PVBOXNETFLTINS pNetFlt = (PVBOXNETFLTINS)NdisIMGetDeviceContext(MiniportAdapterHandle); + NDIS_STATUS Status = NDIS_STATUS_FAILURE; + + LogFlowFunc(("ENTER: pNetFlt (0x%p)\n", pNetFlt)); + + pNetFlt->u.s.WinIf.hMiniport = MiniportAdapterHandle; + Assert(vboxNetFltWinGetOpState(&pNetFlt->u.s.WinIf.MpState) == kVBoxNetDevOpState_Initializing); + /* the MP state should be already set to kVBoxNetDevOpState_Initializing, just a paranoia + * in case NDIS for some reason calls us in some irregular way */ + vboxNetFltWinSetOpState(&pNetFlt->u.s.WinIf.MpState, kVBoxNetDevOpState_Initializing); + + NDIS_MEDIUM enmMedium = pNetFlt->u.s.WinIf.enmMedium; + if (enmMedium == NdisMediumWan) + enmMedium = NdisMedium802_3; + + UINT i = 0; + for (; i < MediumArraySize; i++) + { + if (MediumArray[i] == enmMedium) + { + *SelectedMediumIndex = i; + break; + } + } + + do + { + if (i != MediumArraySize) + { + NdisMSetAttributesEx(MiniportAdapterHandle, pNetFlt, 0, + NDIS_ATTRIBUTE_IGNORE_PACKET_TIMEOUT | + NDIS_ATTRIBUTE_IGNORE_REQUEST_TIMEOUT| + NDIS_ATTRIBUTE_INTERMEDIATE_DRIVER | + NDIS_ATTRIBUTE_DESERIALIZE | + NDIS_ATTRIBUTE_NO_HALT_ON_SUSPEND, + NdisInterfaceInternal /* 0 */); + + pNetFlt->u.s.WinIf.MpIndicatedMediaStatus = NDIS_STATUS_MEDIA_CONNECT; + Assert(vboxNetFltWinGetPowerState(&pNetFlt->u.s.WinIf.MpState) == NdisDeviceStateD3); + vboxNetFltWinSetPowerState(&pNetFlt->u.s.WinIf.MpState, NdisDeviceStateD0); + Assert(pNetFlt->u.s.WinIf.MpState.OpState == kVBoxNetDevOpState_Initializing); + vboxNetFltWinSetOpState(&pNetFlt->u.s.WinIf.MpState, kVBoxNetDevOpState_Initialized); + + Status = NDIS_STATUS_SUCCESS; + break; + } + else + { + Status = NDIS_STATUS_UNSUPPORTED_MEDIA; + } + + Assert(Status != NDIS_STATUS_SUCCESS); + Assert(vboxNetFltWinGetPowerState(&pNetFlt->u.s.WinIf.MpState) == NdisDeviceStateD3); + Assert(pNetFlt->u.s.WinIf.MpState.OpState == kVBoxNetDevOpState_Initializing); + vboxNetFltWinSetOpState(&pNetFlt->u.s.WinIf.MpState, kVBoxNetDevOpState_Deinitialized); + } while (0); + + NdisSetEvent(&pNetFlt->u.s.WinIf.MpInitCompleteEvent); + + LogFlowFunc(("LEAVE: pNetFlt (0x%p), Status (0x%x)\n", pNetFlt, Status)); + + *OpenErrorStatus = Status; + + return Status; +} + +/** + * process the packet send in a "passthru" mode + */ +static NDIS_STATUS vboxNetFltWinSendPassThru(PVBOXNETFLTINS pNetFlt, PNDIS_PACKET pPacket +#ifdef VBOXNETFLT_NO_PACKET_QUEUE + , bool bNetFltActive +#endif + ) +{ + PNDIS_PACKET pMyPacket; + NDIS_STATUS Status = vboxNetFltWinPrepareSendPacket(pNetFlt, pPacket, &pMyPacket); + Assert(Status == NDIS_STATUS_SUCCESS); + if (Status == NDIS_STATUS_SUCCESS) + { +#if !defined(VBOX_LOOPBACK_USEFLAGS) /* || defined(DEBUG_NETFLT_PACKETS) */ +# ifdef VBOXNETFLT_NO_PACKET_QUEUE + if (bNetFltActive) + vboxNetFltWinLbPutSendPacket(pNetFlt, pMyPacket, false /* bFromIntNet */); +# else + /* no need for the loop enqueue & check in a passthru mode , ndis will do everything for us */ +# endif +#endif + NdisSend(&Status, pNetFlt->u.s.WinIf.hBinding, pMyPacket); + if (Status != NDIS_STATUS_PENDING) + { + NdisIMCopySendCompletePerPacketInfo(pPacket, pMyPacket); +#if defined(VBOXNETFLT_NO_PACKET_QUEUE) && !defined(VBOX_LOOPBACK_USEFLAGS) + if (bNetFltActive) + vboxNetFltWinLbRemoveSendPacket(pNetFlt, pMyPacket); +#endif + NdisFreePacket(pMyPacket); + } + } + return Status; +} + +#else /* defined VBOXNETADP */ +DECLHIDDEN(NDIS_STATUS) vboxNetFltWinMpDoDeinitialization(PVBOXNETFLTINS pNetFlt) +{ + uint64_t NanoTS = RTTimeSystemNanoTS(); + + Assert(vboxNetFltWinGetOpState(&pNetFlt->u.s.WinIf.MpState) == kVBoxNetDevOpState_Initialized); + + RTSpinlockAcquire(pNetFlt->hSpinlock); + ASMAtomicUoWriteBool(&pNetFlt->fDisconnectedFromHost, true); + ASMAtomicUoWriteBool(&pNetFlt->fRediscoveryPending, false); + ASMAtomicUoWriteU64(&pNetFlt->NanoTSLastRediscovery, NanoTS); + + vboxNetFltWinSetOpState(&pNetFlt->u.s.WinIf.MpState, kVBoxNetDevOpState_Deinitializing); + + RTSpinlockRelease(pNetFlt->hSpinlock); + + vboxNetFltWinWaitDereference(&pNetFlt->u.s.WinIf.MpState); + + /* check packet pool is empty */ + int cPPUsage = NdisPacketPoolUsage(pNetFlt->u.s.WinIf.hRecvPacketPool); + Assert(cPPUsage == 0); + /* for debugging only, ignore the err in release */ + NOREF(cPPUsage); + + vboxNetFltWinSetOpState(&pNetFlt->u.s.WinIf.MpState, kVBoxNetDevOpState_Deinitialized); + + return NDIS_STATUS_SUCCESS; +} + +static NDIS_STATUS vboxNetFltWinMpReadApplyConfig(PVBOXNETFLTINS pThis, NDIS_HANDLE hMiniportAdapter, + NDIS_HANDLE hWrapperConfigurationContext) +{ + RT_NOREF1(hMiniportAdapter); + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + NDIS_HANDLE hConfiguration; + PNDIS_CONFIGURATION_PARAMETER pParameterValue; + NDIS_STRING strMAC = NDIS_STRING_CONST("MAC"); + RTMAC mac; + + NdisOpenConfiguration( + &Status, + &hConfiguration, + hWrapperConfigurationContext); + Assert(Status == NDIS_STATUS_SUCCESS); + if (Status == NDIS_STATUS_SUCCESS) + { + do + { + int rc; + NDIS_CONFIGURATION_PARAMETER param; + WCHAR MacBuf[13]; + + NdisReadConfiguration(&Status, + &pParameterValue, + hConfiguration, + &strMAC, + NdisParameterString); +// Assert(Status == NDIS_STATUS_SUCCESS); + if (Status == NDIS_STATUS_SUCCESS) + { + + rc = vboxNetFltWinMACFromNdisString(&mac, &pParameterValue->ParameterData.StringData); + AssertRC(rc); + if (RT_SUCCESS(rc)) + { + break; + } + } + + vboxNetFltWinGenerateMACAddress(&mac); + param.ParameterType = NdisParameterString; + param.ParameterData.StringData.Buffer = MacBuf; + param.ParameterData.StringData.MaximumLength = sizeof(MacBuf); + + rc = vboxNetFltWinMAC2NdisString(&mac, ¶m.ParameterData.StringData); + Assert(RT_SUCCESS(rc)); + if (RT_SUCCESS(rc)) + { + NdisWriteConfiguration(&Status, + hConfiguration, + &strMAC, + ¶m); + Assert(Status == NDIS_STATUS_SUCCESS); + if (Status != NDIS_STATUS_SUCCESS) + { + /* ignore the failure */ + Status = NDIS_STATUS_SUCCESS; + } + } + } while (0); + + NdisCloseConfiguration(hConfiguration); + } + else + { + vboxNetFltWinGenerateMACAddress(&mac); + } + + pThis->u.s.MacAddr = mac; + + return NDIS_STATUS_SUCCESS; +} + +DECLHIDDEN(NDIS_STATUS) vboxNetFltWinMpDoInitialization(PVBOXNETFLTINS pNetFlt, NDIS_HANDLE hMiniportAdapter, NDIS_HANDLE hWrapperConfigurationContext) +{ + NDIS_STATUS Status; + pNetFlt->u.s.WinIf.hMiniport = hMiniportAdapter; + + LogFlowFunc(("ENTER: pNetFlt 0x%p\n", pNetFlt)); + + Assert(vboxNetFltWinGetOpState(&pNetFlt->u.s.WinIf.MpState) == kVBoxNetDevOpState_Deinitialized); + vboxNetFltWinSetOpState(&pNetFlt->u.s.WinIf.MpState, kVBoxNetDevOpState_Initializing); + + vboxNetFltWinMpReadApplyConfig(pNetFlt, hMiniportAdapter, hWrapperConfigurationContext); + + NdisMSetAttributesEx(hMiniportAdapter, pNetFlt, + 0, /* CheckForHangTimeInSeconds */ + NDIS_ATTRIBUTE_DESERIALIZE | + NDIS_ATTRIBUTE_NO_HALT_ON_SUSPEND, + NdisInterfaceInternal/* 0 */); + + Assert(vboxNetFltWinGetPowerState(&pNetFlt->u.s.WinIf.MpState) == NdisDeviceStateD3); + vboxNetFltWinSetPowerState(&pNetFlt->u.s.WinIf.MpState, NdisDeviceStateD0); + Assert(vboxNetFltWinGetOpState(&pNetFlt->u.s.WinIf.MpState) == kVBoxNetDevOpState_Initializing); + vboxNetFltWinSetOpState(&pNetFlt->u.s.WinIf.MpState, kVBoxNetDevOpState_Initialized); + + Status = NDIS_STATUS_SUCCESS; + + LogFlowFunc(("pNetFlt 0x%p, Status 0x%x\n", pNetFlt, Status)); + + return Status; +} + +static NDIS_STATUS vboxNetFltWinMpInitialize(OUT PNDIS_STATUS OpenErrorStatus, + OUT PUINT SelectedMediumIndex, + IN PNDIS_MEDIUM MediumArray, + IN UINT MediumArraySize, + IN NDIS_HANDLE MiniportAdapterHandle, + IN NDIS_HANDLE WrapperConfigurationContext) +{ + + NDIS_STATUS Status = NDIS_STATUS_FAILURE; + UINT i = 0; + + LogFlowFuncEnter(); + + for (; i < MediumArraySize; i++) + { + if (MediumArray[i] == NdisMedium802_3) + { + *SelectedMediumIndex = i; + break; + } + } + + if (i != MediumArraySize) + { + PDEVICE_OBJECT pPdo, pFdo; +#define KEY_PREFIX L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Class\\" + UCHAR Buf[512]; + PUCHAR pSuffix; + ULONG cbBuf; + NDIS_STRING RtlStr; + + wcscpy((WCHAR*)Buf, KEY_PREFIX); + pSuffix = Buf + (sizeof(KEY_PREFIX)-2); + + NdisMGetDeviceProperty(MiniportAdapterHandle, + &pPdo, + &pFdo, + NULL, //Next Device Object + NULL, + NULL); + + Status = IoGetDeviceProperty (pPdo, + DevicePropertyDriverKeyName, + sizeof(Buf) - (sizeof(KEY_PREFIX)-2), + pSuffix, + &cbBuf); + if (Status == STATUS_SUCCESS) + { + OBJECT_ATTRIBUTES ObjAttr; + HANDLE hDrvKey; + RtlStr.Buffer=(WCHAR*)Buf; + RtlStr.Length=(USHORT)cbBuf - 2 + sizeof(KEY_PREFIX) - 2; + RtlStr.MaximumLength=sizeof(Buf); + + InitializeObjectAttributes(&ObjAttr, &RtlStr, OBJ_CASE_INSENSITIVE, NULL, NULL); + + Status = ZwOpenKey(&hDrvKey, KEY_READ, &ObjAttr); + if (Status == STATUS_SUCCESS) + { + static UNICODE_STRING NetCfgInstanceIdValue = NDIS_STRING_CONST("NetCfgInstanceId"); +// UCHAR valBuf[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + RTUUID_STR_LENGTH*2 + 10]; +// ULONG cLength = sizeof(valBuf); +#define NAME_PREFIX L"\\DEVICE\\" + PKEY_VALUE_PARTIAL_INFORMATION pInfo = (PKEY_VALUE_PARTIAL_INFORMATION)Buf; + Status = ZwQueryValueKey(hDrvKey, + &NetCfgInstanceIdValue, + KeyValuePartialInformation, + pInfo, + sizeof(Buf), + &cbBuf); + if (Status == STATUS_SUCCESS) + { + if (pInfo->Type == REG_SZ && pInfo->DataLength > 2) + { + WCHAR *pName; + Status = vboxNetFltWinMemAlloc((PVOID*)&pName, pInfo->DataLength + sizeof(NAME_PREFIX)); + if (Status == STATUS_SUCCESS) + { + PVBOXNETFLTINS pNetFlt; + wcscpy(pName, NAME_PREFIX); + wcscpy(pName+(sizeof(NAME_PREFIX)-2)/2, (WCHAR*)pInfo->Data); + RtlStr.Buffer=pName; + RtlStr.Length = (USHORT)pInfo->DataLength - 2 + sizeof(NAME_PREFIX) - 2; + RtlStr.MaximumLength = (USHORT)pInfo->DataLength + sizeof(NAME_PREFIX); + + Status = vboxNetFltWinPtInitBind(&pNetFlt, MiniportAdapterHandle, &RtlStr, WrapperConfigurationContext); + + if (Status == STATUS_SUCCESS) + { + Assert(vboxNetFltWinGetOpState(&pNetFlt->u.s.WinIf.MpState) == kVBoxNetDevOpState_Initialized); + vboxNetFltWinSetOpState(&pNetFlt->u.s.WinIf.MpState, kVBoxNetDevOpState_Initialized); +#if 0 + NdisMIndicateStatus(pNetFlt->u.s.WinIf.hMiniport, + NDIS_STATUS_MEDIA_CONNECT, + (PVOID)NULL, + 0); +#endif + } + else + { + Assert(vboxNetFltWinGetOpState(&pNetFlt->u.s.WinIf.MpState) == kVBoxNetDevOpState_Deinitialized); + vboxNetFltWinSetOpState(&pNetFlt->u.s.WinIf.MpState, kVBoxNetDevOpState_Deinitialized); + } + + vboxNetFltWinMemFree(pName); + + } + } + else + { + Status = NDIS_STATUS_FAILURE; + } + } + } + } + } + else + { + Status = NDIS_STATUS_UNSUPPORTED_MEDIA; + } + + /** @todo */ + *OpenErrorStatus = Status; + + LogFlowFunc(("LEAVE: Status (0x%x)\n", Status)); + + return Status; +} +#endif + +static VOID vboxNetFltWinMpSendPackets(IN NDIS_HANDLE hMiniportAdapterContext, + IN PPNDIS_PACKET pPacketArray, + IN UINT cNumberOfPackets) +{ + PVBOXNETFLTINS pNetFlt = (PVBOXNETFLTINS)hMiniportAdapterContext; + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + bool bNetFltActive; + + LogFlowFunc(("ENTER: pNetFlt (0x%p)\n", pNetFlt)); + + Assert(cNumberOfPackets); + + if (vboxNetFltWinIncReferenceWinIfNetFlt(pNetFlt, cNumberOfPackets, &bNetFltActive)) + { + uint32_t cAdaptRefs = cNumberOfPackets; + uint32_t cNetFltRefs; + uint32_t cPassThruRefs; + if (bNetFltActive) + { + cNetFltRefs = cNumberOfPackets; + cPassThruRefs = 0; + } + else + { + cPassThruRefs = cNumberOfPackets; + cNetFltRefs = 0; + } + + for (UINT i = 0; i < cNumberOfPackets; i++) + { + PNDIS_PACKET pPacket; + + pPacket = pPacketArray[i]; + + if (!cNetFltRefs +#ifdef VBOXNETFLT_NO_PACKET_QUEUE + || !vboxNetFltWinPostIntnet(pNetFlt, pPacket, VBOXNETFLT_PACKET_SRC_HOST) +#else + || (fStatus = vboxNetFltWinQuEnqueuePacket(pNetFlt, pPacket, VBOXNETFLT_PACKET_SRC_HOST)) != NDIS_STATUS_SUCCESS +#endif + ) + { +#ifndef VBOXNETADP + Status = vboxNetFltWinSendPassThru(pNetFlt, pPacket +#ifdef VBOXNETFLT_NO_PACKET_QUEUE + , !!cNetFltRefs +#endif + ); +#else + if (!cNetFltRefs) + { +# ifdef VBOXNETADP_REPORT_DISCONNECTED + Status = NDIS_STATUS_MEDIA_DISCONNECT; + STATISTIC_INCREASE(pNetFlt->u.s.WinIf.cTxError); +# else + Status = NDIS_STATUS_SUCCESS; +# endif + } +#endif + + if (Status != NDIS_STATUS_PENDING) + { + NdisMSendComplete(pNetFlt->u.s.WinIf.hMiniport, pPacket, Status); + } + else + { + cAdaptRefs--; + } + } + else + { +#ifdef VBOXNETFLT_NO_PACKET_QUEUE + NdisMSendComplete(pNetFlt->u.s.WinIf.hMiniport, pPacket, NDIS_STATUS_SUCCESS); +#else + cAdaptRefs--; + cNetFltRefs--; +#endif + } + } + + if (cNetFltRefs) + { + vboxNetFltWinDecReferenceNetFlt(pNetFlt, cNetFltRefs); + } + else if (cPassThruRefs) + { + vboxNetFltWinDecReferenceModePassThru(pNetFlt, cPassThruRefs); + } + if (cAdaptRefs) + { + vboxNetFltWinDecReferenceWinIf(pNetFlt, cAdaptRefs); + } + } + else + { + NDIS_HANDLE h = pNetFlt->u.s.WinIf.hMiniport; + AssertFailed(); + if (h) + { + for (UINT i = 0; i < cNumberOfPackets; i++) + { + PNDIS_PACKET pPacket; + pPacket = pPacketArray[i]; + NdisMSendComplete(h, pPacket, NDIS_STATUS_FAILURE); + } + } + } + + LogFlowFunc(("LEAVE: pNetFlt (0x%p)\n", pNetFlt)); +} + +#ifndef VBOXNETADP +static UINT vboxNetFltWinMpRequestStatePrep(PVBOXNETFLTINS pNetFlt, NDIS_STATUS *pStatus) +{ + Assert(!pNetFlt->u.s.WinIf.StateFlags.fRequestInfo); + + if (vboxNetFltWinGetOpState(&pNetFlt->u.s.WinIf.PtState) > kVBoxNetDevOpState_Initialized /* protocol unbind in progress */ + || vboxNetFltWinGetPowerState(&pNetFlt->u.s.WinIf.MpState) > NdisDeviceStateD0) + { + *pStatus = NDIS_STATUS_FAILURE; + return 0; + } + + RTSpinlockAcquire(pNetFlt->hSpinlock); + Assert(!pNetFlt->u.s.WinIf.StateFlags.fRequestInfo); + if (vboxNetFltWinGetOpState(&pNetFlt->u.s.WinIf.PtState) > kVBoxNetDevOpState_Initialized /* protocol unbind in progress */ + || vboxNetFltWinGetPowerState(&pNetFlt->u.s.WinIf.MpState) > NdisDeviceStateD0) + { + RTSpinlockRelease(pNetFlt->hSpinlock); + *pStatus = NDIS_STATUS_FAILURE; + return 0; + } + + if ((vboxNetFltWinGetPowerState(&pNetFlt->u.s.WinIf.PtState) > NdisDeviceStateD0) + && !pNetFlt->u.s.WinIf.StateFlags.fStandBy) + { + pNetFlt->u.s.WinIf.StateFlags.fRequestInfo = VBOXNDISREQUEST_INPROGRESS | VBOXNDISREQUEST_QUEUED; + RTSpinlockRelease(pNetFlt->hSpinlock); + *pStatus = NDIS_STATUS_PENDING; + return VBOXNDISREQUEST_INPROGRESS | VBOXNDISREQUEST_QUEUED; + } + + if (pNetFlt->u.s.WinIf.StateFlags.fStandBy) + { + RTSpinlockRelease(pNetFlt->hSpinlock); + *pStatus = NDIS_STATUS_FAILURE; + return 0; + } + + pNetFlt->u.s.WinIf.StateFlags.fRequestInfo = VBOXNDISREQUEST_INPROGRESS; + + RTSpinlockRelease(pNetFlt->hSpinlock); + + *pStatus = NDIS_STATUS_SUCCESS; + return VBOXNDISREQUEST_INPROGRESS; +} + +static NDIS_STATUS vboxNetFltWinMpRequestPostQuery(PVBOXNETFLTINS pNetFlt) +{ + if (pNetFlt->u.s.WinIf.PassDownRequest.DATA.QUERY_INFORMATION.Oid == OID_GEN_CURRENT_PACKET_FILTER && VBOXNETFLT_PROMISCUOUS_SUPPORTED(pNetFlt)) + { + bool fNetFltActive; + const bool fWinIfActive = vboxNetFltWinReferenceWinIfNetFlt(pNetFlt, &fNetFltActive); + + Assert(pNetFlt->u.s.WinIf.PassDownRequest.DATA.QUERY_INFORMATION.InformationBuffer); + Assert(!pNetFlt->u.s.WinIf.StateFlags.fProcessingPacketFilter); + + if (fNetFltActive) + { + /* netflt is active, simply return the cached value */ + *((PULONG)pNetFlt->u.s.WinIf.PassDownRequest.DATA.QUERY_INFORMATION.InformationBuffer) = pNetFlt->u.s.WinIf.fUpperProtocolSetFilter; + + /* we've intercepted the query and completed it */ + vboxNetFltWinMpRequestStateComplete(pNetFlt); + + vboxNetFltWinDereferenceNetFlt(pNetFlt); + vboxNetFltWinDereferenceWinIf(pNetFlt); + + return NDIS_STATUS_SUCCESS; + } + else if (fWinIfActive) + { + pNetFlt->u.s.WinIf.StateFlags.fProcessingPacketFilter = 1; + pNetFlt->u.s.WinIf.StateFlags.fPPFNetFlt = 0; + /* we're cleaning it in RequestComplete */ + } + } + + NDIS_STATUS Status; + /* issue the request */ + NdisRequest(&Status, pNetFlt->u.s.WinIf.hBinding, &pNetFlt->u.s.WinIf.PassDownRequest); + if (Status != NDIS_STATUS_PENDING) + { + vboxNetFltWinPtRequestComplete(pNetFlt, &pNetFlt->u.s.WinIf.PassDownRequest, Status); + Status = NDIS_STATUS_PENDING; + } + + return Status; +} + +static NDIS_STATUS vboxNetFltWinMpQueryInformation(IN NDIS_HANDLE MiniportAdapterContext, + IN NDIS_OID Oid, + IN PVOID InformationBuffer, + IN ULONG InformationBufferLength, + OUT PULONG BytesWritten, + OUT PULONG BytesNeeded) +{ + PVBOXNETFLTINS pNetFlt = (PVBOXNETFLTINS)MiniportAdapterContext; + NDIS_STATUS Status = NDIS_STATUS_FAILURE; + + LogFlowFunc(("ENTER: pNetFlt (0x%p), Oid (%s)\n", pNetFlt, vboxNetFltWinMpDumpOid(Oid))); + + /* fist check if this is the oid we want to pass down */ + switch (Oid) + { + case OID_PNP_QUERY_POWER: + Status = NDIS_STATUS_SUCCESS; + break; + case OID_TCP_TASK_OFFLOAD: + case OID_GEN_SUPPORTED_GUIDS: + Status = NDIS_STATUS_NOT_SUPPORTED; + break; + default: + { + /* the oid is to be passed down, + * check the device state if we can do it + * and update device state accordingly */ + UINT uOp = vboxNetFltWinMpRequestStatePrep(pNetFlt, &Status); + if (uOp) + { + /* save the request info */ + pNetFlt->u.s.WinIf.PassDownRequest.RequestType = NdisRequestQueryInformation; + pNetFlt->u.s.WinIf.PassDownRequest.DATA.QUERY_INFORMATION.Oid = Oid; + pNetFlt->u.s.WinIf.PassDownRequest.DATA.QUERY_INFORMATION.InformationBuffer = InformationBuffer; + pNetFlt->u.s.WinIf.PassDownRequest.DATA.QUERY_INFORMATION.InformationBufferLength = InformationBufferLength; + pNetFlt->u.s.WinIf.pcPDRBytesNeeded = BytesNeeded; + pNetFlt->u.s.WinIf.pcPDRBytesRW = BytesWritten; + + /* the oid can be processed */ + if (!(uOp & VBOXNDISREQUEST_QUEUED)) + { + Status = vboxNetFltWinMpRequestPostQuery(pNetFlt); + } + } + break; + } + } + + LogFlowFunc(("LEAVE: pNetFlt (0x%p), Oid (%s), Status (0x%x)\n", pNetFlt, vboxNetFltWinMpDumpOid(Oid), Status)); + + return Status; +} + +#endif /* ifndef VBOXNETADP*/ + +static NDIS_STATUS vboxNetFltWinMpHandlePowerState(PVBOXNETFLTINS pNetFlt, NDIS_DEVICE_POWER_STATE enmState) +{ + if (vboxNetFltWinGetPowerState(&pNetFlt->u.s.WinIf.MpState) > NdisDeviceStateD0 + && enmState != NdisDeviceStateD0) + { + /* invalid state transformation */ + AssertFailed(); + return NDIS_STATUS_FAILURE; + } + +#ifndef VBOXNETADP + if (vboxNetFltWinGetPowerState(&pNetFlt->u.s.WinIf.MpState) == NdisDeviceStateD0 + && enmState > NdisDeviceStateD0) + { + pNetFlt->u.s.WinIf.StateFlags.fStandBy = TRUE; + } + + if (vboxNetFltWinGetPowerState(&pNetFlt->u.s.WinIf.MpState) > NdisDeviceStateD0 + && enmState == NdisDeviceStateD0) + { + pNetFlt->u.s.WinIf.StateFlags.fStandBy = FALSE; + } +#endif + + vboxNetFltWinSetPowerState(&pNetFlt->u.s.WinIf.MpState, enmState); + +#ifndef VBOXNETADP + if (pNetFlt->u.s.WinIf.StateFlags.fStandBy == FALSE) + { + if (pNetFlt->u.s.WinIf.MpIndicatedMediaStatus != pNetFlt->u.s.WinIf.MpUnindicatedMediaStatus) + { + NdisMIndicateStatus(pNetFlt->u.s.WinIf.hMiniport, pNetFlt->u.s.WinIf.MpUnindicatedMediaStatus, NULL, 0); + NdisMIndicateStatusComplete(pNetFlt->u.s.WinIf.hMiniport); + pNetFlt->u.s.WinIf.MpIndicatedMediaStatus = pNetFlt->u.s.WinIf.MpUnindicatedMediaStatus; + } + } + else + { + pNetFlt->u.s.WinIf.MpUnindicatedMediaStatus = pNetFlt->u.s.WinIf.MpIndicatedMediaStatus; + } +#endif + + return NDIS_STATUS_SUCCESS; +} + +#ifndef VBOXNETADP +static NDIS_STATUS vboxNetFltWinMpRequestPostSet(PVBOXNETFLTINS pNetFlt) +{ + if (pNetFlt->u.s.WinIf.PassDownRequest.DATA.SET_INFORMATION.Oid == OID_GEN_CURRENT_PACKET_FILTER && VBOXNETFLT_PROMISCUOUS_SUPPORTED(pNetFlt)) + { + /* need to disable cleaning promiscuous here ?? */ + bool fNetFltActive; + const bool fWinIfActive = vboxNetFltWinReferenceWinIfNetFlt(pNetFlt, &fNetFltActive); + + Assert(pNetFlt->u.s.WinIf.PassDownRequest.DATA.SET_INFORMATION.InformationBuffer); + Assert(!pNetFlt->u.s.WinIf.StateFlags.fProcessingPacketFilter); + + if (fNetFltActive) + { + Assert(fWinIfActive); + + /* netflt is active, update the cached value */ + /** @todo in case we are are not in promiscuous now, we are issuing a request. + * what should we do in case of a failure? + * i.e. should we update the fUpperProtocolSetFilter in completion routine in this case? etc. */ + pNetFlt->u.s.WinIf.fUpperProtocolSetFilter = *((PULONG)pNetFlt->u.s.WinIf.PassDownRequest.DATA.SET_INFORMATION.InformationBuffer); + pNetFlt->u.s.WinIf.StateFlags.fUpperProtSetFilterInitialized = TRUE; + + if (!(pNetFlt->u.s.WinIf.fOurSetFilter & NDIS_PACKET_TYPE_PROMISCUOUS)) + { + pNetFlt->u.s.WinIf.fSetFilterBuffer = NDIS_PACKET_TYPE_PROMISCUOUS; + pNetFlt->u.s.WinIf.PassDownRequest.DATA.SET_INFORMATION.InformationBuffer = &pNetFlt->u.s.WinIf.fSetFilterBuffer; + pNetFlt->u.s.WinIf.PassDownRequest.DATA.SET_INFORMATION.InformationBufferLength = sizeof (pNetFlt->u.s.WinIf.fSetFilterBuffer); + pNetFlt->u.s.WinIf.StateFlags.fProcessingPacketFilter = 1; + pNetFlt->u.s.WinIf.StateFlags.fPPFNetFlt = 1; + /* we'll do dereferencing in request complete */ + } + else + { + vboxNetFltWinDereferenceNetFlt(pNetFlt); + vboxNetFltWinDereferenceWinIf(pNetFlt); + + /* we've intercepted the query and completed it */ + vboxNetFltWinMpRequestStateComplete(pNetFlt); + return NDIS_STATUS_SUCCESS; + } + } + else if (fWinIfActive) + { + pNetFlt->u.s.WinIf.StateFlags.fProcessingPacketFilter = 1; + pNetFlt->u.s.WinIf.StateFlags.fPPFNetFlt = 0; + /* dereference on completion */ + } + } + + NDIS_STATUS Status; + + NdisRequest(&Status, pNetFlt->u.s.WinIf.hBinding, &pNetFlt->u.s.WinIf.PassDownRequest); + if (Status != NDIS_STATUS_PENDING) + { + vboxNetFltWinPtRequestComplete(pNetFlt, &pNetFlt->u.s.WinIf.PassDownRequest, Status); + } + + return Status; +} + +DECLHIDDEN(NDIS_STATUS) vboxNetFltWinMpRequestPost(PVBOXNETFLTINS pNetFlt) +{ + switch (pNetFlt->u.s.WinIf.PassDownRequest.RequestType) + { + case NdisRequestQueryInformation: + return vboxNetFltWinMpRequestPostQuery(pNetFlt); + case NdisRequestSetInformation: + return vboxNetFltWinMpRequestPostSet(pNetFlt); + default: + AssertBreakpoint(); + return NDIS_STATUS_FAILURE; + } +} + +static NDIS_STATUS vboxNetFltWinMpSetInformation(IN NDIS_HANDLE MiniportAdapterContext, + IN NDIS_OID Oid, + IN PVOID InformationBuffer, + IN ULONG InformationBufferLength, + OUT PULONG BytesRead, + OUT PULONG BytesNeeded) +{ + PVBOXNETFLTINS pNetFlt = (PVBOXNETFLTINS)MiniportAdapterContext; + NDIS_STATUS Status = NDIS_STATUS_FAILURE; + + LogFlowFunc(("ENTER: pNetFlt (0x%p), Oid (%s)\n", pNetFlt, vboxNetFltWinMpDumpOid(Oid))); + + switch (Oid) + { + case OID_PNP_SET_POWER: + { + if (InformationBufferLength >= sizeof (NDIS_DEVICE_POWER_STATE)) + { + NDIS_DEVICE_POWER_STATE *penmState = (NDIS_DEVICE_POWER_STATE*)InformationBuffer; + Status = vboxNetFltWinMpHandlePowerState(pNetFlt, *penmState); + } + else + { + Status = NDIS_STATUS_INVALID_LENGTH; + } + + if (Status == NDIS_STATUS_SUCCESS) + { + *BytesRead = sizeof (NDIS_DEVICE_POWER_STATE); + *BytesNeeded = 0; + } + else + { + *BytesRead = 0; + *BytesNeeded = sizeof (NDIS_DEVICE_POWER_STATE); + } + break; + } + default: + { + /* the oid is to be passed down, + * check the device state if we can do it + * and update device state accordingly */ + UINT uOp = vboxNetFltWinMpRequestStatePrep(pNetFlt, &Status); + if (uOp) + { + /* save the request info */ + pNetFlt->u.s.WinIf.PassDownRequest.RequestType = NdisRequestSetInformation; + pNetFlt->u.s.WinIf.PassDownRequest.DATA.SET_INFORMATION.Oid = Oid; + pNetFlt->u.s.WinIf.PassDownRequest.DATA.SET_INFORMATION.InformationBuffer = InformationBuffer; + pNetFlt->u.s.WinIf.PassDownRequest.DATA.SET_INFORMATION.InformationBufferLength = InformationBufferLength; + pNetFlt->u.s.WinIf.pcPDRBytesNeeded = BytesNeeded; + pNetFlt->u.s.WinIf.pcPDRBytesRW = BytesRead; + + /* the oid can be processed */ + if (!(uOp & VBOXNDISREQUEST_QUEUED)) + { + Status = vboxNetFltWinMpRequestPostSet(pNetFlt); + } + } + break; + } + } + + LogFlowFunc(("LEAVE: pNetFlt (0x%p), Oid (%s), Status (0x%x)\n", pNetFlt, vboxNetFltWinMpDumpOid(Oid), Status)); + + return Status; +} +#else +static NDIS_OID g_vboxNetFltWinMpSupportedOids[] = +{ + OID_GEN_SUPPORTED_LIST, + OID_GEN_HARDWARE_STATUS, + OID_GEN_MEDIA_SUPPORTED, + OID_GEN_MEDIA_IN_USE, + OID_GEN_MAXIMUM_LOOKAHEAD, + OID_GEN_CURRENT_LOOKAHEAD, + OID_GEN_MAXIMUM_FRAME_SIZE, + OID_GEN_MAXIMUM_TOTAL_SIZE, + OID_GEN_TRANSMIT_BLOCK_SIZE, + OID_GEN_RECEIVE_BLOCK_SIZE, + OID_GEN_MAC_OPTIONS, + OID_GEN_LINK_SPEED, + OID_GEN_TRANSMIT_BUFFER_SPACE, + OID_GEN_RECEIVE_BUFFER_SPACE, + OID_GEN_VENDOR_ID, + OID_GEN_VENDOR_DESCRIPTION, + OID_GEN_VENDOR_DRIVER_VERSION, + OID_GEN_DRIVER_VERSION, + OID_GEN_MAXIMUM_SEND_PACKETS, + OID_GEN_MEDIA_CONNECT_STATUS, + OID_GEN_CURRENT_PACKET_FILTER, + OID_PNP_CAPABILITIES, + OID_PNP_QUERY_POWER, + OID_GEN_XMIT_OK, + OID_GEN_RCV_OK, + OID_GEN_XMIT_ERROR, + OID_GEN_RCV_ERROR, + OID_GEN_RCV_NO_BUFFER, + OID_GEN_RCV_CRC_ERROR, + OID_GEN_TRANSMIT_QUEUE_LENGTH, + OID_PNP_SET_POWER, + OID_802_3_PERMANENT_ADDRESS, + OID_802_3_CURRENT_ADDRESS, + OID_802_3_MULTICAST_LIST, + OID_802_3_MAC_OPTIONS, + OID_802_3_MAXIMUM_LIST_SIZE, + OID_802_3_RCV_ERROR_ALIGNMENT, + OID_802_3_XMIT_ONE_COLLISION, + OID_802_3_XMIT_MORE_COLLISIONS, + OID_802_3_XMIT_DEFERRED, + OID_802_3_XMIT_MAX_COLLISIONS, + OID_802_3_RCV_OVERRUN, + OID_802_3_XMIT_UNDERRUN, + OID_802_3_XMIT_HEARTBEAT_FAILURE, + OID_802_3_XMIT_TIMES_CRS_LOST, + OID_802_3_XMIT_LATE_COLLISIONS, +}; + +static NDIS_STATUS vboxNetFltWinMpQueryInformation(IN NDIS_HANDLE MiniportAdapterContext, + IN NDIS_OID Oid, + IN PVOID InformationBuffer, + IN ULONG InformationBufferLength, + OUT PULONG BytesWritten, + OUT PULONG BytesNeeded) +{ + /* static data */ + static const NDIS_HARDWARE_STATUS enmHwStatus = NdisHardwareStatusReady; + static const NDIS_MEDIUM enmMedium = NdisMedium802_3; + static NDIS_PNP_CAPABILITIES PnPCaps = {0}; + static BOOLEAN bPnPCapsInited = FALSE; + + PVBOXNETFLTINS pNetFlt = (PVBOXNETFLTINS)MiniportAdapterContext; + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + ULONG64 u64Info = 0; + ULONG u32Info = 0; + USHORT u16Info = 0; + /* default is 4bytes */ + const void* pvInfo = (void*)&u32Info; + ULONG cbInfo = sizeof (u32Info); + + LogFlowFunc(("ENTER: pNetFlt (0x%p), Oid (%s)\n", pNetFlt, vboxNetFltWinMpDumpOid(Oid))); + + *BytesWritten = 0; + *BytesNeeded = 0; + + switch (Oid) + { + case OID_GEN_SUPPORTED_LIST: + pvInfo = g_vboxNetFltWinMpSupportedOids; + cbInfo = sizeof (g_vboxNetFltWinMpSupportedOids); + break; + + case OID_GEN_HARDWARE_STATUS: + pvInfo = &enmHwStatus; + cbInfo = sizeof (NDIS_HARDWARE_STATUS); + break; + + case OID_GEN_MEDIA_SUPPORTED: + case OID_GEN_MEDIA_IN_USE: + pvInfo = &enmMedium; + cbInfo = sizeof (NDIS_MEDIUM); + break; + + case OID_GEN_MAXIMUM_LOOKAHEAD: + case OID_GEN_CURRENT_LOOKAHEAD: + u32Info = VBOXNETADP_MAX_LOOKAHEAD_SIZE; + break; + + case OID_GEN_MAXIMUM_FRAME_SIZE: + u32Info = VBOXNETADP_MAX_PACKET_SIZE - VBOXNETADP_HEADER_SIZE; + break; + + case OID_GEN_MAXIMUM_TOTAL_SIZE: + case OID_GEN_TRANSMIT_BLOCK_SIZE: + case OID_GEN_RECEIVE_BLOCK_SIZE: + u32Info = VBOXNETADP_MAX_PACKET_SIZE; + break; + + case OID_GEN_MAC_OPTIONS: + u32Info = NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA | + NDIS_MAC_OPTION_TRANSFERS_NOT_PEND | + NDIS_MAC_OPTION_NO_LOOPBACK; + break; + + case OID_GEN_LINK_SPEED: + u32Info = VBOXNETADP_LINK_SPEED; + break; + + case OID_GEN_TRANSMIT_BUFFER_SPACE: + case OID_GEN_RECEIVE_BUFFER_SPACE: + u32Info = VBOXNETADP_MAX_PACKET_SIZE * VBOXNETFLT_PACKET_INFO_POOL_SIZE; + break; + + case OID_GEN_VENDOR_ID: + u32Info = VBOXNETADP_VENDOR_ID; + break; + + case OID_GEN_VENDOR_DESCRIPTION: + pvInfo = VBOXNETADP_VENDOR_DESC; + cbInfo = sizeof (VBOXNETADP_VENDOR_DESC); + break; + + case OID_GEN_VENDOR_DRIVER_VERSION: + u32Info = VBOXNETADP_VENDOR_DRIVER_VERSION; + break; + + case OID_GEN_DRIVER_VERSION: + u16Info = (USHORT)((VBOXNETFLT_VERSION_MP_NDIS_MAJOR << 8) + VBOXNETFLT_VERSION_MP_NDIS_MINOR); + pvInfo = (PVOID)&u16Info; + cbInfo = sizeof (USHORT); + break; + + case OID_GEN_MAXIMUM_SEND_PACKETS: + u32Info = VBOXNETFLT_PACKET_INFO_POOL_SIZE; + break; + + case OID_GEN_MEDIA_CONNECT_STATUS: +#ifdef VBOXNETADP_REPORT_DISCONNECTED + { + bool bNetFltActive; + bool bActive = vboxNetFltWinReferenceWinIfNetFltFromAdapt(pNetFlt, bNetFltActive); + if (bActive && bNetFltActive) + { + u32Info = NdisMediaStateConnected; + } + else + { + u32Info = NdisMediaStateDisconnected; + } + + if (bActive) + { + vboxNetFltWinDereferenceWinIf(pNetFlt); + } + if (bNetFltActive) + { + vboxNetFltWinDereferenceNetFlt(pNetFlt); + } + else + { + vboxNetFltWinDereferenceModePassThru(pNetFlt); + } + } +#else + u32Info = NdisMediaStateConnected; +#endif + break; + + case OID_GEN_CURRENT_PACKET_FILTER: + u32Info = NDIS_PACKET_TYPE_BROADCAST + | NDIS_PACKET_TYPE_DIRECTED + | NDIS_PACKET_TYPE_ALL_FUNCTIONAL + | NDIS_PACKET_TYPE_ALL_LOCAL + | NDIS_PACKET_TYPE_GROUP + | NDIS_PACKET_TYPE_MULTICAST; + break; + + case OID_PNP_CAPABILITIES: + if (!bPnPCapsInited) + { + PnPCaps.WakeUpCapabilities.MinMagicPacketWakeUp = NdisDeviceStateUnspecified; + PnPCaps.WakeUpCapabilities.MinPatternWakeUp = NdisDeviceStateUnspecified; + bPnPCapsInited = TRUE; + } + cbInfo = sizeof (NDIS_PNP_CAPABILITIES); + pvInfo = &PnPCaps; + + break; + + case OID_PNP_QUERY_POWER: + Status = NDIS_STATUS_SUCCESS; + break; + + case OID_GEN_XMIT_OK: + u64Info = pNetFlt->u.s.WinIf.cTxSuccess; + pvInfo = &u64Info; + if (InformationBufferLength >= sizeof (ULONG64) || InformationBufferLength == 0) + { + cbInfo = sizeof (ULONG64); + } + else + { + cbInfo = sizeof (ULONG); + } + *BytesNeeded = sizeof (ULONG64); + break; + + case OID_GEN_RCV_OK: + u64Info = pNetFlt->u.s.WinIf.cRxSuccess; + pvInfo = &u64Info; + if (InformationBufferLength >= sizeof (ULONG64) || InformationBufferLength == 0) + { + cbInfo = sizeof (ULONG64); + } + else + { + cbInfo = sizeof (ULONG); + } + *BytesNeeded = sizeof (ULONG64); + break; + + case OID_GEN_XMIT_ERROR: + u32Info = pNetFlt->u.s.WinIf.cTxError; + break; + + case OID_GEN_RCV_ERROR: + u32Info = pNetFlt->u.s.WinIf.cRxError; + break; + + case OID_GEN_RCV_NO_BUFFER: + case OID_GEN_RCV_CRC_ERROR: + u32Info = 0; + break; + + case OID_GEN_TRANSMIT_QUEUE_LENGTH: + u32Info = VBOXNETFLT_PACKET_INFO_POOL_SIZE; + break; + + case OID_802_3_PERMANENT_ADDRESS: + pvInfo = &pNetFlt->u.s.MacAddr; + cbInfo = VBOXNETADP_ETH_ADDRESS_LENGTH; + break; + + case OID_802_3_CURRENT_ADDRESS: + pvInfo = &pNetFlt->u.s.MacAddr; + cbInfo = VBOXNETADP_ETH_ADDRESS_LENGTH; + break; + + case OID_802_3_MAXIMUM_LIST_SIZE: + u32Info = VBOXNETADP_MAX_MCAST_LIST; + break; + + case OID_802_3_MAC_OPTIONS: + case OID_802_3_RCV_ERROR_ALIGNMENT: + case OID_802_3_XMIT_ONE_COLLISION: + case OID_802_3_XMIT_MORE_COLLISIONS: + case OID_802_3_XMIT_DEFERRED: + case OID_802_3_XMIT_MAX_COLLISIONS: + case OID_802_3_RCV_OVERRUN: + case OID_802_3_XMIT_UNDERRUN: + case OID_802_3_XMIT_HEARTBEAT_FAILURE: + case OID_802_3_XMIT_TIMES_CRS_LOST: + case OID_802_3_XMIT_LATE_COLLISIONS: + u32Info = 0; + break; + + default: + Status = NDIS_STATUS_NOT_SUPPORTED; + break; + } + + if (Status == NDIS_STATUS_SUCCESS) + { + if (cbInfo <= InformationBufferLength) + { + *BytesWritten = cbInfo; + if (cbInfo) + NdisMoveMemory(InformationBuffer, pvInfo, cbInfo); + } + else + { + *BytesNeeded = cbInfo; + Status = NDIS_STATUS_INVALID_LENGTH; + } + } + + + LogFlowFunc(("LEAVE: pNetFlt (0x%p), Oid (%s), Status (0x%x)\n", pNetFlt, vboxNetFltWinMpDumpOid(Oid), Status)); + + return Status; +} + +static NDIS_STATUS vboxNetFltWinMpSetInformation(IN NDIS_HANDLE MiniportAdapterContext, + IN NDIS_OID Oid, + IN PVOID InformationBuffer, + IN ULONG InformationBufferLength, + OUT PULONG BytesRead, + OUT PULONG BytesNeeded) +{ + PVBOXNETFLTINS pNetFlt = (PVBOXNETFLTINS) MiniportAdapterContext; + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + + LogFlowFunc(("ENTER: pNetFlt (0x%p), Oid (%s)\n", pNetFlt, vboxNetFltWinMpDumpOid(Oid))); + + *BytesRead = 0; + *BytesNeeded = 0; + + switch (Oid) + { + case OID_802_3_MULTICAST_LIST: + *BytesRead = InformationBufferLength; + if (InformationBufferLength % VBOXNETADP_ETH_ADDRESS_LENGTH) + { + Status = NDIS_STATUS_INVALID_LENGTH; + break; + } + + if (InformationBufferLength > (VBOXNETADP_MAX_MCAST_LIST * VBOXNETADP_ETH_ADDRESS_LENGTH)) + { + Status = NDIS_STATUS_MULTICAST_FULL; + *BytesNeeded = VBOXNETADP_MAX_MCAST_LIST * VBOXNETADP_ETH_ADDRESS_LENGTH; + break; + } + break; + + case OID_GEN_CURRENT_PACKET_FILTER: + if (InformationBufferLength != sizeof (ULONG)) + { + *BytesNeeded = sizeof (ULONG); + Status = NDIS_STATUS_INVALID_LENGTH; + break; + } + + *BytesRead = InformationBufferLength; + + break; + + case OID_GEN_CURRENT_LOOKAHEAD: + if (InformationBufferLength != sizeof (ULONG)){ + *BytesNeeded = sizeof(ULONG); + Status = NDIS_STATUS_INVALID_LENGTH; + break; + } + + break; + + case OID_PNP_SET_POWER: + if (InformationBufferLength >= sizeof(NDIS_DEVICE_POWER_STATE)) + { + NDIS_DEVICE_POWER_STATE *penmState = (NDIS_DEVICE_POWER_STATE*)InformationBuffer; + Status = vboxNetFltWinMpHandlePowerState(pNetFlt, *penmState); + } + else + { + Status = NDIS_STATUS_INVALID_LENGTH; + } + + if (Status == NDIS_STATUS_SUCCESS) + { + *BytesRead = sizeof (NDIS_DEVICE_POWER_STATE); + *BytesNeeded = 0; + } + else + { + *BytesRead = 0; + *BytesNeeded = sizeof (NDIS_DEVICE_POWER_STATE); + } + break; + + default: + Status = NDIS_STATUS_INVALID_OID; + break; + } + + LogFlowFunc(("LEAVE: pNetFlt (0x%p), Oid (%s), Status (0x%x)\n", pNetFlt, vboxNetFltWinMpDumpOid(Oid), Status)); + + return Status; +} + +#endif + +#define VBOXNETFLTDUMP_STRCASE(_t) \ + case _t: return #_t; +#define VBOXNETFLTDUMP_STRCASE_UNKNOWN() \ + default: /*AssertFailed();*/ return "Unknown"; + +static const char* vboxNetFltWinMpDumpOid(ULONG oid) +{ + switch (oid) + { + VBOXNETFLTDUMP_STRCASE(OID_GEN_SUPPORTED_LIST) + VBOXNETFLTDUMP_STRCASE(OID_GEN_HARDWARE_STATUS) + VBOXNETFLTDUMP_STRCASE(OID_GEN_MEDIA_SUPPORTED) + VBOXNETFLTDUMP_STRCASE(OID_GEN_MEDIA_IN_USE) + VBOXNETFLTDUMP_STRCASE(OID_GEN_MAXIMUM_LOOKAHEAD) + VBOXNETFLTDUMP_STRCASE(OID_GEN_MAXIMUM_FRAME_SIZE) + VBOXNETFLTDUMP_STRCASE(OID_GEN_LINK_SPEED) + VBOXNETFLTDUMP_STRCASE(OID_GEN_TRANSMIT_BUFFER_SPACE) + VBOXNETFLTDUMP_STRCASE(OID_GEN_RECEIVE_BUFFER_SPACE) + VBOXNETFLTDUMP_STRCASE(OID_GEN_TRANSMIT_BLOCK_SIZE) + VBOXNETFLTDUMP_STRCASE(OID_GEN_RECEIVE_BLOCK_SIZE) + VBOXNETFLTDUMP_STRCASE(OID_GEN_VENDOR_ID) + VBOXNETFLTDUMP_STRCASE(OID_GEN_VENDOR_DESCRIPTION) + VBOXNETFLTDUMP_STRCASE(OID_GEN_CURRENT_PACKET_FILTER) + VBOXNETFLTDUMP_STRCASE(OID_GEN_CURRENT_LOOKAHEAD) + VBOXNETFLTDUMP_STRCASE(OID_GEN_DRIVER_VERSION) + VBOXNETFLTDUMP_STRCASE(OID_GEN_MAXIMUM_TOTAL_SIZE) + VBOXNETFLTDUMP_STRCASE(OID_GEN_PROTOCOL_OPTIONS) + VBOXNETFLTDUMP_STRCASE(OID_GEN_MAC_OPTIONS) + VBOXNETFLTDUMP_STRCASE(OID_GEN_MEDIA_CONNECT_STATUS) + VBOXNETFLTDUMP_STRCASE(OID_GEN_MAXIMUM_SEND_PACKETS) + VBOXNETFLTDUMP_STRCASE(OID_GEN_VENDOR_DRIVER_VERSION) + VBOXNETFLTDUMP_STRCASE(OID_GEN_SUPPORTED_GUIDS) + VBOXNETFLTDUMP_STRCASE(OID_GEN_NETWORK_LAYER_ADDRESSES) + VBOXNETFLTDUMP_STRCASE(OID_GEN_TRANSPORT_HEADER_OFFSET) + VBOXNETFLTDUMP_STRCASE(OID_GEN_MACHINE_NAME) + VBOXNETFLTDUMP_STRCASE(OID_GEN_RNDIS_CONFIG_PARAMETER) + VBOXNETFLTDUMP_STRCASE(OID_GEN_VLAN_ID) + VBOXNETFLTDUMP_STRCASE(OID_GEN_MEDIA_CAPABILITIES) + VBOXNETFLTDUMP_STRCASE(OID_GEN_PHYSICAL_MEDIUM) + VBOXNETFLTDUMP_STRCASE(OID_GEN_XMIT_OK) + VBOXNETFLTDUMP_STRCASE(OID_GEN_RCV_OK) + VBOXNETFLTDUMP_STRCASE(OID_GEN_XMIT_ERROR) + VBOXNETFLTDUMP_STRCASE(OID_GEN_RCV_ERROR) + VBOXNETFLTDUMP_STRCASE(OID_GEN_RCV_NO_BUFFER) + VBOXNETFLTDUMP_STRCASE(OID_GEN_DIRECTED_BYTES_XMIT) + VBOXNETFLTDUMP_STRCASE(OID_GEN_DIRECTED_FRAMES_XMIT) + VBOXNETFLTDUMP_STRCASE(OID_GEN_MULTICAST_BYTES_XMIT) + VBOXNETFLTDUMP_STRCASE(OID_GEN_MULTICAST_FRAMES_XMIT) + VBOXNETFLTDUMP_STRCASE(OID_GEN_BROADCAST_BYTES_XMIT) + VBOXNETFLTDUMP_STRCASE(OID_GEN_BROADCAST_FRAMES_XMIT) + VBOXNETFLTDUMP_STRCASE(OID_GEN_DIRECTED_BYTES_RCV) + VBOXNETFLTDUMP_STRCASE(OID_GEN_DIRECTED_FRAMES_RCV) + VBOXNETFLTDUMP_STRCASE(OID_GEN_MULTICAST_BYTES_RCV) + VBOXNETFLTDUMP_STRCASE(OID_GEN_MULTICAST_FRAMES_RCV) + VBOXNETFLTDUMP_STRCASE(OID_GEN_BROADCAST_BYTES_RCV) + VBOXNETFLTDUMP_STRCASE(OID_GEN_BROADCAST_FRAMES_RCV) + VBOXNETFLTDUMP_STRCASE(OID_GEN_RCV_CRC_ERROR) + VBOXNETFLTDUMP_STRCASE(OID_GEN_TRANSMIT_QUEUE_LENGTH) + VBOXNETFLTDUMP_STRCASE(OID_GEN_GET_TIME_CAPS) + VBOXNETFLTDUMP_STRCASE(OID_GEN_GET_NETCARD_TIME) + VBOXNETFLTDUMP_STRCASE(OID_GEN_NETCARD_LOAD) + VBOXNETFLTDUMP_STRCASE(OID_GEN_DEVICE_PROFILE) + VBOXNETFLTDUMP_STRCASE(OID_GEN_INIT_TIME_MS) + VBOXNETFLTDUMP_STRCASE(OID_GEN_RESET_COUNTS) + VBOXNETFLTDUMP_STRCASE(OID_GEN_MEDIA_SENSE_COUNTS) + VBOXNETFLTDUMP_STRCASE(OID_PNP_CAPABILITIES) + VBOXNETFLTDUMP_STRCASE(OID_PNP_SET_POWER) + VBOXNETFLTDUMP_STRCASE(OID_PNP_QUERY_POWER) + VBOXNETFLTDUMP_STRCASE(OID_PNP_ADD_WAKE_UP_PATTERN) + VBOXNETFLTDUMP_STRCASE(OID_PNP_REMOVE_WAKE_UP_PATTERN) + VBOXNETFLTDUMP_STRCASE(OID_PNP_ENABLE_WAKE_UP) + VBOXNETFLTDUMP_STRCASE(OID_802_3_PERMANENT_ADDRESS) + VBOXNETFLTDUMP_STRCASE(OID_802_3_CURRENT_ADDRESS) + VBOXNETFLTDUMP_STRCASE(OID_802_3_MULTICAST_LIST) + VBOXNETFLTDUMP_STRCASE(OID_802_3_MAXIMUM_LIST_SIZE) + VBOXNETFLTDUMP_STRCASE(OID_802_3_MAC_OPTIONS) + VBOXNETFLTDUMP_STRCASE(OID_802_3_RCV_ERROR_ALIGNMENT) + VBOXNETFLTDUMP_STRCASE(OID_802_3_XMIT_ONE_COLLISION) + VBOXNETFLTDUMP_STRCASE(OID_802_3_XMIT_MORE_COLLISIONS) + VBOXNETFLTDUMP_STRCASE(OID_802_3_XMIT_DEFERRED) + VBOXNETFLTDUMP_STRCASE(OID_802_3_XMIT_MAX_COLLISIONS) + VBOXNETFLTDUMP_STRCASE(OID_802_3_RCV_OVERRUN) + VBOXNETFLTDUMP_STRCASE(OID_802_3_XMIT_UNDERRUN) + VBOXNETFLTDUMP_STRCASE(OID_802_3_XMIT_HEARTBEAT_FAILURE) + VBOXNETFLTDUMP_STRCASE(OID_802_3_XMIT_TIMES_CRS_LOST) + VBOXNETFLTDUMP_STRCASE(OID_802_3_XMIT_LATE_COLLISIONS) + VBOXNETFLTDUMP_STRCASE(OID_TCP_TASK_OFFLOAD) + VBOXNETFLTDUMP_STRCASE(OID_TCP_TASK_IPSEC_ADD_SA) + VBOXNETFLTDUMP_STRCASE(OID_TCP_TASK_IPSEC_DELETE_SA) + VBOXNETFLTDUMP_STRCASE(OID_TCP_SAN_SUPPORT) + VBOXNETFLTDUMP_STRCASE(OID_TCP_TASK_IPSEC_ADD_UDPESP_SA) + VBOXNETFLTDUMP_STRCASE(OID_TCP_TASK_IPSEC_DELETE_UDPESP_SA) + VBOXNETFLTDUMP_STRCASE_UNKNOWN() + } +} + +DECLHIDDEN(VOID) vboxNetFltWinMpReturnPacket(IN NDIS_HANDLE hMiniportAdapterContext, IN PNDIS_PACKET pPacket) +{ + PVBOXNETFLTINS pNetFlt = (PVBOXNETFLTINS)hMiniportAdapterContext; + PVBOXNETFLT_PKTRSVD_MP pInfo = (PVBOXNETFLT_PKTRSVD_MP)pPacket->MiniportReserved; + PNDIS_PACKET pOrigPacket = pInfo->pOrigPacket; + PVOID pBufToFree = pInfo->pBufToFree; + + LogFlowFunc(("ENTER: pNetFlt (0x%p)\n", pNetFlt)); + + if (pOrigPacket) + { + /* the packet was sent from underlying miniport */ + NdisFreePacket(pPacket); + NdisReturnPackets(&pOrigPacket, 1); + } + else + { + /* the packet was sent from IntNet or it is a packet we allocated on PtReceive for TransferData processing */ + vboxNetFltWinFreeSGNdisPacket(pPacket, !pBufToFree /* bFreeMem */); + } + + if (pBufToFree) + { + vboxNetFltWinMemFree(pBufToFree); + } + + vboxNetFltWinDereferenceWinIf(pNetFlt); + + LogFlowFunc(("LEAVE: pNetFlt (0x%p)\n", pNetFlt)); +} + +static NDIS_STATUS vboxNetFltWinMpTransferData(OUT PNDIS_PACKET Packet, + OUT PUINT BytesTransferred, + IN NDIS_HANDLE hContext, + IN NDIS_HANDLE MiniportReceiveContext, + IN UINT ByteOffset, + IN UINT BytesToTransfer) +{ +#ifndef VBOXNETADP + PVBOXNETFLTINS pNetFlt = (PVBOXNETFLTINS)hContext; + NDIS_STATUS Status; + + LogFlowFunc(("ENTER: pNetFlt (0x%p)\n", pNetFlt)); + + if ( vboxNetFltWinGetPowerState(&pNetFlt->u.s.WinIf.PtState) != NdisDeviceStateD0 + || vboxNetFltWinGetPowerState(&pNetFlt->u.s.WinIf.MpState) != NdisDeviceStateD0) + { + LogFlowFunc(("LEAVE: pNetFlt (0x%p), Status (0x%x)\n", pNetFlt, NDIS_STATUS_FAILURE)); + return NDIS_STATUS_FAILURE; + } + + NdisTransferData(&Status, pNetFlt->u.s.WinIf.hBinding, MiniportReceiveContext, + ByteOffset, BytesToTransfer, Packet, BytesTransferred); + + LogFlowFunc(("LEAVE: pNetFlt (0x%p), Status (0x%x)\n", pNetFlt, Status)); + return Status; + +#else + RT_NOREF6(Packet, BytesTransferred, hContext, MiniportReceiveContext, ByteOffset, BytesToTransfer); + LogFlowFunc(("ENTER: pNetFlt (0x%p)\n", hContext)); + /* should never be here */ + AssertFailed(); + LogFlowFunc(("LEAVE: pNetFlt (0x%p), Status (0x%x)\n", hContext, NDIS_STATUS_FAILURE)); + return NDIS_STATUS_FAILURE; +#endif +} + +static void vboxNetFltWinMpHalt(IN NDIS_HANDLE hContext) +{ + PVBOXNETFLTINS pNetFlt = (PVBOXNETFLTINS)hContext; + NDIS_STATUS Status; + + LogFlowFunc(("ENTER: pNetFlt (0x%p)\n", pNetFlt)); + +#ifndef VBOXNETADP + if (vboxNetFltWinGetWinIfState(pNetFlt) == kVBoxWinIfState_Disconnecting) + { + Assert(vboxNetFltWinGetOpState(&pNetFlt->u.s.WinIf.MpState) == kVBoxNetDevOpState_Deinitializing); + vboxNetFltWinSetOpState(&pNetFlt->u.s.WinIf.MpState, kVBoxNetDevOpState_Deinitializing); + + vboxNetFltWinPtCloseInterface(pNetFlt, &Status); + + Assert(vboxNetFltWinGetOpState(&pNetFlt->u.s.WinIf.PtState) == kVBoxNetDevOpState_Deinitializing); + vboxNetFltWinSetOpState(&pNetFlt->u.s.WinIf.PtState, kVBoxNetDevOpState_Deinitialized); + vboxNetFltWinSetOpState(&pNetFlt->u.s.WinIf.MpState, kVBoxNetDevOpState_Deinitialized); + } + else +#endif + { + /* we're NOT called from protocolUnbinAdapter, perform a full disconnect */ + Assert(vboxNetFltWinGetOpState(&pNetFlt->u.s.WinIf.MpState) == kVBoxNetDevOpState_Initialized); +#ifndef VBOXNETADP + AssertBreakpoint(); +#endif + Status = vboxNetFltWinDetachFromInterface(pNetFlt, false); + Assert(Status == NDIS_STATUS_SUCCESS); + } + + LogFlowFunc(("LEAVE: pNetFlt (0x%p)\n", pNetFlt)); +} + +/** + * register the miniport edge + */ +DECLHIDDEN(NDIS_STATUS) vboxNetFltWinMpRegister(PVBOXNETFLTGLOBALS_MP pGlobalsMp, PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegistryPathStr) +{ + NDIS_MINIPORT_CHARACTERISTICS MpChars; + + NdisMInitializeWrapper(&pGlobalsMp->hNdisWrapper, pDriverObject, pRegistryPathStr, NULL); + + NdisZeroMemory(&MpChars, sizeof (MpChars)); + + MpChars.MajorNdisVersion = VBOXNETFLT_VERSION_MP_NDIS_MAJOR; + MpChars.MinorNdisVersion = VBOXNETFLT_VERSION_MP_NDIS_MINOR; + + MpChars.HaltHandler = vboxNetFltWinMpHalt; + MpChars.InitializeHandler = vboxNetFltWinMpInitialize; + MpChars.QueryInformationHandler = vboxNetFltWinMpQueryInformation; + MpChars.SetInformationHandler = vboxNetFltWinMpSetInformation; + MpChars.TransferDataHandler = vboxNetFltWinMpTransferData; + MpChars.ReturnPacketHandler = vboxNetFltWinMpReturnPacket; + MpChars.SendPacketsHandler = vboxNetFltWinMpSendPackets; + +#ifndef VBOXNETADP + NDIS_STATUS Status = NdisIMRegisterLayeredMiniport(pGlobalsMp->hNdisWrapper, &MpChars, sizeof (MpChars), &pGlobalsMp->hMiniport); +#else + NDIS_STATUS Status = NdisMRegisterMiniport(pGlobalsMp->hNdisWrapper, &MpChars, sizeof (MpChars)); +#endif + Assert(Status == NDIS_STATUS_SUCCESS); + if (Status == NDIS_STATUS_SUCCESS) + { + NdisMRegisterUnloadHandler(pGlobalsMp->hNdisWrapper, vboxNetFltWinUnload); + } + + return Status; +} + +/** + * deregister the miniport edge + */ +DECLHIDDEN(VOID) vboxNetFltWinMpDeregister(PVBOXNETFLTGLOBALS_MP pGlobalsMp) +{ +#ifndef VBOXNETADP + NdisIMDeregisterLayeredMiniport(pGlobalsMp->hMiniport); +#endif + NdisTerminateWrapper(pGlobalsMp->hNdisWrapper, NULL); + + NdisZeroMemory(pGlobalsMp, sizeof (*pGlobalsMp)); +} + +#ifndef VBOXNETADP + +DECLHIDDEN(NDIS_STATUS) vboxNetFltWinMpInitializeDevideInstance(PVBOXNETFLTINS pThis) +{ + NDIS_STATUS Status; + Assert(vboxNetFltWinGetOpState(&pThis->u.s.WinIf.MpState) == kVBoxNetDevOpState_Deinitialized); + vboxNetFltWinSetOpState(&pThis->u.s.WinIf.MpState, kVBoxNetDevOpState_Initializing); + + Status = NdisIMInitializeDeviceInstanceEx(g_VBoxNetFltGlobalsWin.Mp.hMiniport, + &pThis->u.s.WinIf.MpDeviceName, + pThis); + if (Status == NDIS_STATUS_SUCCESS) + { + if (pThis->u.s.WinIf.OpenCloseStatus == NDIS_STATUS_SUCCESS) + { + return NDIS_STATUS_SUCCESS; + } + AssertBreakpoint(); + vboxNetFltWinMpDeInitializeDeviceInstance(pThis, &Status); + Assert(vboxNetFltWinGetOpState(&pThis->u.s.WinIf.MpState) == kVBoxNetDevOpState_Deinitialized); + vboxNetFltWinSetOpState(&pThis->u.s.WinIf.MpState, kVBoxNetDevOpState_Deinitialized); + return pThis->u.s.WinIf.OpenCloseStatus; + } + + Assert(vboxNetFltWinGetOpState(&pThis->u.s.WinIf.MpState) == kVBoxNetDevOpState_Deinitialized); + vboxNetFltWinSetOpState(&pThis->u.s.WinIf.MpState, kVBoxNetDevOpState_Deinitialized); + + return Status; +} + +DECLHIDDEN(bool) vboxNetFltWinMpDeInitializeDeviceInstance(PVBOXNETFLTINS pThis, PNDIS_STATUS pStatus) +{ + NDIS_STATUS Status; + + if (vboxNetFltWinGetOpState(&pThis->u.s.WinIf.MpState) == kVBoxNetDevOpState_Initializing) + { + Status = NdisIMCancelInitializeDeviceInstance(g_VBoxNetFltGlobalsWin.Mp.hMiniport, &pThis->u.s.WinIf.MpDeviceName); + if (Status == NDIS_STATUS_SUCCESS) + { + /* we've canceled the initialization successfully */ + Assert(pThis->u.s.WinIf.hMiniport == NULL); + Assert(vboxNetFltWinGetOpState(&pThis->u.s.WinIf.MpState) == kVBoxNetDevOpState_Deinitialized); + vboxNetFltWinSetOpState(&pThis->u.s.WinIf.MpState, kVBoxNetDevOpState_Deinitialized); + } + else + NdisWaitEvent(&pThis->u.s.WinIf.MpInitCompleteEvent, 0); + } + else + Status = NDIS_STATUS_SUCCESS; + + Assert( vboxNetFltWinGetOpState(&pThis->u.s.WinIf.MpState) == kVBoxNetDevOpState_Initialized + || vboxNetFltWinGetOpState(&pThis->u.s.WinIf.MpState) == kVBoxNetDevOpState_Deinitialized); + if (vboxNetFltWinGetOpState(&pThis->u.s.WinIf.MpState) == kVBoxNetDevOpState_Initialized) + { + vboxNetFltWinSetOpState(&pThis->u.s.WinIf.MpState, kVBoxNetDevOpState_Deinitializing); + + Status = NdisIMDeInitializeDeviceInstance(pThis->u.s.WinIf.hMiniport); + + vboxNetFltWinSetOpState(&pThis->u.s.WinIf.MpState, kVBoxNetDevOpState_Deinitialized); + if (Status != NDIS_STATUS_SUCCESS) + Status = NDIS_STATUS_FAILURE; + + *pStatus = Status; + return true; + } + + Assert(vboxNetFltWinGetOpState(&pThis->u.s.WinIf.MpState) == kVBoxNetDevOpState_Deinitialized); + vboxNetFltWinSetOpState(&pThis->u.s.WinIf.MpState, kVBoxNetDevOpState_Deinitialized); + + *pStatus = Status; + return false; +} + +#endif /* !VBOXNETADP */ diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltM-win.h b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltM-win.h new file mode 100644 index 00000000..c68fcdb9 --- /dev/null +++ b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltM-win.h @@ -0,0 +1,57 @@ +/* $Id: VBoxNetFltM-win.h $ */ +/** @file + * VBoxNetFltM-win.h - Bridged Networking Driver, Windows Specific Code - Miniport edge API + */ + +/* + * Copyright (C) 2011-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * 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. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE 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. + */ + +#ifndef VBOX_INCLUDED_SRC_VBoxNetFlt_win_drv_VBoxNetFltM_win_h +#define VBOX_INCLUDED_SRC_VBoxNetFlt_win_drv_VBoxNetFltM_win_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +DECLHIDDEN(NDIS_STATUS) vboxNetFltWinMpRegister(PVBOXNETFLTGLOBALS_MP pGlobalsMp, PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegistryPathStr); +DECLHIDDEN(VOID) vboxNetFltWinMpDeregister(PVBOXNETFLTGLOBALS_MP pGlobalsMp); +DECLHIDDEN(VOID) vboxNetFltWinMpReturnPacket(IN NDIS_HANDLE hMiniportAdapterContext, IN PNDIS_PACKET pPacket); + +#ifdef VBOXNETADP +DECLHIDDEN(NDIS_STATUS) vboxNetFltWinMpDoInitialization(PVBOXNETFLTINS pThis, NDIS_HANDLE hMiniportAdapter, NDIS_HANDLE hWrapperConfigurationContext); +DECLHIDDEN(NDIS_STATUS) vboxNetFltWinMpDoDeinitialization(PVBOXNETFLTINS pThis); + +#else + +DECLHIDDEN(NDIS_STATUS) vboxNetFltWinMpInitializeDevideInstance(PVBOXNETFLTINS pThis); +DECLHIDDEN(bool) vboxNetFltWinMpDeInitializeDeviceInstance(PVBOXNETFLTINS pThis, PNDIS_STATUS pStatus); + +DECLINLINE(VOID) vboxNetFltWinMpRequestStateComplete(PVBOXNETFLTINS pNetFlt) +{ + RTSpinlockAcquire(pNetFlt->hSpinlock); + pNetFlt->u.s.WinIf.StateFlags.fRequestInfo = 0; + RTSpinlockRelease(pNetFlt->hSpinlock); +} + +DECLHIDDEN(NDIS_STATUS) vboxNetFltWinMpRequestPost(PVBOXNETFLTINS pNetFlt); +#endif + +#endif /* !VBOX_INCLUDED_SRC_VBoxNetFlt_win_drv_VBoxNetFltM_win_h */ + diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltM.inf b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltM.inf new file mode 100644 index 00000000..1718fdb2 --- /dev/null +++ b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltM.inf @@ -0,0 +1,78 @@ +; $Id: VBoxNetFltM.inf $ +;; @file +; VBoxNetFltM.inf - VirtualBox Bridged Networking Driver inf file Miniport edge +; + +; +; Copyright (C) 2011-2019 Oracle Corporation +; +; This file is part of VirtualBox Open Source Edition (OSE), as +; available from http://www.virtualbox.org. This file is free software; +; you can redistribute it and/or modify it under the terms of the GNU +; 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. +; +; The contents of this file may alternatively be used under the terms +; of the Common Development and Distribution License Version 1.0 +; (CDDL) only, as it comes in the "COPYING.CDDL" file of the +; VirtualBox OSE 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. +; + +[Version] +signature = "$Windows NT$" +;cat CatalogFile = VBoxNetFlt.cat +Class = Net +ClassGUID = {4d36e972-e325-11ce-bfc1-08002be10318} +Provider = %Provider% +;DriverPackageType=NdisImMiniport +;DriverPackageDisplayName=%VBoxNetFltMP_Desc% +;edit-DriverVer=08/13/2008,1.1.0.1 + +[ControlFlags] +ExcludeFromSelect = sun_VBoxNetFltmp + +[DestinationDirs] +DefaultDestDir=12 +; No files to copy + +[Manufacturer] +%Provider% = VBox,NTx86,NTamd64 + +[VBox] +%VBoxNetFltMP_Desc% = VBoxNetFltMP.ndi, sun_VBoxNetFltmp + +[VBox.NTx86] +%VBoxNetFltMP_Desc% = VBoxNetFltMP.ndi, sun_VBoxNetFltmp + +[VBox.NTamd64] +%VBoxNetFltMP_Desc% = VBoxNetFltMP.ndi, sun_VBoxNetFltmp + +[VBoxNetFltMP.ndi] +Characteristics = 0x29 ;NCF_NOT_USER_REMOVABLE | NCF_VIRTUAL | NCF_HIDDEN +CopyFiles = + +[VBoxNetFltMP.ndi.Services] +AddService = VBoxNetFlt,0x2, VBoxNetFltMP.AddService + +[VBoxNetFltMP.AddService] +ServiceType = 1 ;SERVICE_KERNEL_DRIVER +StartType = 3 ;SERVICE_DEMAND_START +ErrorControl = 1 ;SERVICE_ERROR_NORMAL +ServiceBinary = %12%\VBoxNetFlt.sys + +[VBoxNetFltMP.AddService.AddReg] + +[Strings] +Provider = "Oracle Corporation" +VBoxNetFltMP_Desc = "VirtualBox Bridged Networking Driver Miniport" + +[SourceDisksNames] + +[SourceDisksFiles] + diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltP-win.cpp b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltP-win.cpp new file mode 100644 index 00000000..c2a7b7dc --- /dev/null +++ b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltP-win.cpp @@ -0,0 +1,1585 @@ +/* $Id: VBoxNetFltP-win.cpp $ */ +/** @file + * VBoxNetFltP-win.cpp - Bridged Networking Driver, Windows Specific Code. + * Protocol edge + */ +/* + * Copyright (C) 2011-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * 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. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE 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. + */ +#include "VBoxNetFltCmn-win.h" + +#ifdef VBOXNETADP +# error "No protocol edge" +#endif + +#define VBOXNETFLT_PT_STATUS_IS_FILTERED(_s) (\ + (_s) == NDIS_STATUS_MEDIA_CONNECT \ + || (_s) == NDIS_STATUS_MEDIA_DISCONNECT \ + ) + +/** + * performs binding to the given adapter + */ +DECLHIDDEN(NDIS_STATUS) vboxNetFltWinPtDoBinding(PVBOXNETFLTINS pThis, PNDIS_STRING pOurDeviceName, PNDIS_STRING pBindToDeviceName) +{ + Assert(pThis->u.s.WinIf.PtState.PowerState == NdisDeviceStateD3); + Assert(pThis->u.s.WinIf.PtState.OpState == kVBoxNetDevOpState_Deinitialized); + Assert(KeGetCurrentIrql() == PASSIVE_LEVEL); + + vboxNetFltWinSetOpState(&pThis->u.s.WinIf.PtState, kVBoxNetDevOpState_Initializing); + + NDIS_STATUS Status = vboxNetFltWinCopyString(&pThis->u.s.WinIf.MpDeviceName, pOurDeviceName); + Assert (Status == NDIS_STATUS_SUCCESS); + if (Status == NDIS_STATUS_SUCCESS) + { + vboxNetFltWinSetPowerState(&pThis->u.s.WinIf.PtState, NdisDeviceStateD0); + pThis->u.s.WinIf.OpenCloseStatus = NDIS_STATUS_SUCCESS; + + UINT iMedium; + NDIS_STATUS TmpStatus; + NDIS_MEDIUM aenmNdisMedium[] = + { + /* Ethernet */ + NdisMedium802_3, + /* Wan */ + NdisMediumWan + }; + + NdisResetEvent(&pThis->u.s.WinIf.OpenCloseEvent); + + NdisOpenAdapter(&Status, &TmpStatus, &pThis->u.s.WinIf.hBinding, &iMedium, + aenmNdisMedium, RT_ELEMENTS(aenmNdisMedium), + g_VBoxNetFltGlobalsWin.Pt.hProtocol, + pThis, + pBindToDeviceName, + 0, /* IN UINT OpenOptions, (reserved, should be NULL) */ + NULL /* IN PSTRING AddressingInformation OPTIONAL */ + ); + Assert(Status == NDIS_STATUS_PENDING || Status == STATUS_SUCCESS); + if (Status == NDIS_STATUS_PENDING) + { + NdisWaitEvent(&pThis->u.s.WinIf.OpenCloseEvent, 0); + Status = pThis->u.s.WinIf.OpenCloseStatus; + } + + Assert(Status == NDIS_STATUS_SUCCESS); + if (Status == NDIS_STATUS_SUCCESS) + { + Assert(pThis->u.s.WinIf.hBinding); + pThis->u.s.WinIf.enmMedium = aenmNdisMedium[iMedium]; + vboxNetFltWinSetOpState(&pThis->u.s.WinIf.PtState, kVBoxNetDevOpState_Initialized); + + Status = vboxNetFltWinMpInitializeDevideInstance(pThis); + Assert(Status == NDIS_STATUS_SUCCESS); + if (Status == NDIS_STATUS_SUCCESS) + { + return NDIS_STATUS_SUCCESS; + } + else + { + LogRelFunc(("vboxNetFltWinMpInitializeDevideInstance failed, Status 0x%x\n", Status)); + } + + vboxNetFltWinSetOpState(&pThis->u.s.WinIf.PtState, kVBoxNetDevOpState_Deinitializing); + vboxNetFltWinPtCloseInterface(pThis, &TmpStatus); + Assert(TmpStatus == NDIS_STATUS_SUCCESS); + vboxNetFltWinSetOpState(&pThis->u.s.WinIf.PtState, kVBoxNetDevOpState_Deinitialized); + } + else + { + LogRelFunc(("NdisOpenAdapter failed, Status (0x%x)", Status)); + } + + vboxNetFltWinSetOpState(&pThis->u.s.WinIf.PtState, kVBoxNetDevOpState_Deinitialized); + pThis->u.s.WinIf.hBinding = NULL; + } + + return Status; +} + +static VOID vboxNetFltWinPtBindAdapter(OUT PNDIS_STATUS pStatus, + IN NDIS_HANDLE hBindContext, + IN PNDIS_STRING pDeviceNameStr, + IN PVOID pvSystemSpecific1, + IN PVOID pvSystemSpecific2) +{ + LogFlowFuncEnter(); + RT_NOREF2(hBindContext, pvSystemSpecific2); + + NDIS_STATUS Status; + NDIS_HANDLE hConfig = NULL; + + NdisOpenProtocolConfiguration(&Status, &hConfig, (PNDIS_STRING)pvSystemSpecific1); + Assert(Status == NDIS_STATUS_SUCCESS); + if (Status == NDIS_STATUS_SUCCESS) + { + PNDIS_CONFIGURATION_PARAMETER pParam; + NDIS_STRING UppedBindStr = NDIS_STRING_CONST("UpperBindings"); + NdisReadConfiguration(&Status, &pParam, hConfig, &UppedBindStr, NdisParameterString); + Assert(Status == NDIS_STATUS_SUCCESS); + if (Status == NDIS_STATUS_SUCCESS) + { + PVBOXNETFLTINS pNetFlt; + Status = vboxNetFltWinPtInitBind(&pNetFlt, &pParam->ParameterData.StringData, pDeviceNameStr); + Assert(Status == NDIS_STATUS_SUCCESS); + } + + NdisCloseConfiguration(hConfig); + } + + *pStatus = Status; + + LogFlowFunc(("LEAVE: Status 0x%x\n", Status)); +} + +static VOID vboxNetFltWinPtOpenAdapterComplete(IN NDIS_HANDLE hProtocolBindingContext, IN NDIS_STATUS Status, IN NDIS_STATUS OpenErrorStatus) +{ + PVBOXNETFLTINS pNetFlt = (PVBOXNETFLTINS)hProtocolBindingContext; + RT_NOREF1(OpenErrorStatus); + + LogFlowFunc(("ENTER: pNetFlt (0x%p), Status (0x%x), OpenErrorStatus(0x%x)\n", pNetFlt, Status, OpenErrorStatus)); + Assert(pNetFlt->u.s.WinIf.OpenCloseStatus == NDIS_STATUS_SUCCESS); + Assert(Status == NDIS_STATUS_SUCCESS); + if (pNetFlt->u.s.WinIf.OpenCloseStatus == NDIS_STATUS_SUCCESS) + { + pNetFlt->u.s.WinIf.OpenCloseStatus = Status; + Assert(Status == NDIS_STATUS_SUCCESS); + if (Status != NDIS_STATUS_SUCCESS) + LogRelFunc(("Open Complete status is 0x%x", Status)); + } + else + LogRelFunc(("Adapter maintained status is 0x%x", pNetFlt->u.s.WinIf.OpenCloseStatus)); + NdisSetEvent(&pNetFlt->u.s.WinIf.OpenCloseEvent); + LogFlowFunc(("LEAVE: pNetFlt (0x%p), Status (0x%x), OpenErrorStatus(0x%x)\n", pNetFlt, Status, OpenErrorStatus)); +} + +static void vboxNetFltWinPtRequestsWaitComplete(PVBOXNETFLTINS pNetFlt) +{ + /* wait for request to complete */ + while (vboxNetFltWinAtomicUoReadWinState(pNetFlt->u.s.WinIf.StateFlags).fRequestInfo == VBOXNDISREQUEST_INPROGRESS) + { + vboxNetFltWinSleep(2); + } + + /* + * If the below miniport is going to low power state, complete the queued request + */ + RTSpinlockAcquire(pNetFlt->hSpinlock); + if (pNetFlt->u.s.WinIf.StateFlags.fRequestInfo & VBOXNDISREQUEST_QUEUED) + { + /* mark the request as InProgress before posting it to RequestComplete */ + pNetFlt->u.s.WinIf.StateFlags.fRequestInfo = VBOXNDISREQUEST_INPROGRESS; + RTSpinlockRelease(pNetFlt->hSpinlock); + vboxNetFltWinPtRequestComplete(pNetFlt, &pNetFlt->u.s.WinIf.PassDownRequest, NDIS_STATUS_FAILURE); + } + else + { + RTSpinlockRelease(pNetFlt->hSpinlock); + } +} + +DECLHIDDEN(NDIS_STATUS) vboxNetFltWinPtDoUnbinding(PVBOXNETFLTINS pNetFlt, bool bOnUnbind) +{ + NDIS_STATUS Status; + uint64_t NanoTS = RTTimeSystemNanoTS(); + int cPPUsage; + + LogFlowFunc(("ENTER: pNetFlt 0x%p\n", pNetFlt)); + + Assert(KeGetCurrentIrql() == PASSIVE_LEVEL); + + Assert(vboxNetFltWinGetOpState(&pNetFlt->u.s.WinIf.PtState) == kVBoxNetDevOpState_Initialized); + + RTSpinlockAcquire(pNetFlt->hSpinlock); + + ASMAtomicUoWriteBool(&pNetFlt->fDisconnectedFromHost, true); + ASMAtomicUoWriteBool(&pNetFlt->fRediscoveryPending, false); + ASMAtomicUoWriteU64(&pNetFlt->NanoTSLastRediscovery, NanoTS); + + vboxNetFltWinSetOpState(&pNetFlt->u.s.WinIf.PtState, kVBoxNetDevOpState_Deinitializing); + if (!bOnUnbind) + { + vboxNetFltWinSetOpState(&pNetFlt->u.s.WinIf.MpState, kVBoxNetDevOpState_Deinitializing); + } + + RTSpinlockRelease(pNetFlt->hSpinlock); + + vboxNetFltWinPtRequestsWaitComplete(pNetFlt); + + vboxNetFltWinWaitDereference(&pNetFlt->u.s.WinIf.MpState); + vboxNetFltWinWaitDereference(&pNetFlt->u.s.WinIf.PtState); + + /* check packet pool is empty */ + cPPUsage = NdisPacketPoolUsage(pNetFlt->u.s.WinIf.hSendPacketPool); + Assert(cPPUsage == 0); + cPPUsage = NdisPacketPoolUsage(pNetFlt->u.s.WinIf.hRecvPacketPool); + Assert(cPPUsage == 0); + /* for debugging only, ignore the err in release */ + NOREF(cPPUsage); + + if (!bOnUnbind || !vboxNetFltWinMpDeInitializeDeviceInstance(pNetFlt, &Status)) + { + vboxNetFltWinPtCloseInterface(pNetFlt, &Status); + vboxNetFltWinSetOpState(&pNetFlt->u.s.WinIf.PtState, kVBoxNetDevOpState_Deinitialized); + + if (!bOnUnbind) + { + Assert(vboxNetFltWinGetOpState(&pNetFlt->u.s.WinIf.MpState) == kVBoxNetDevOpState_Deinitializing); + vboxNetFltWinSetOpState(&pNetFlt->u.s.WinIf.MpState, kVBoxNetDevOpState_Deinitialized); + } + else + { + Assert(vboxNetFltWinGetOpState(&pNetFlt->u.s.WinIf.MpState) == kVBoxNetDevOpState_Deinitialized); + } + } + else + { + Assert(vboxNetFltWinGetOpState(&pNetFlt->u.s.WinIf.MpState) == kVBoxNetDevOpState_Deinitialized); + } + + LogFlowFunc(("LEAVE: pNetFlt 0x%p\n", pNetFlt)); + + return Status; +} + +static VOID vboxNetFltWinPtUnbindAdapter(OUT PNDIS_STATUS pStatus, + IN NDIS_HANDLE hContext, + IN NDIS_HANDLE hUnbindContext) +{ + PVBOXNETFLTINS pNetFlt = (PVBOXNETFLTINS)hContext; + RT_NOREF1(hUnbindContext); + + LogFlowFunc(("ENTER: pNetFlt (0x%p)\n", pNetFlt)); + + *pStatus = vboxNetFltWinDetachFromInterface(pNetFlt, true); + Assert(*pStatus == NDIS_STATUS_SUCCESS); + + LogFlowFunc(("LEAVE: pNetFlt (0x%p)\n", pNetFlt)); +} + +static VOID vboxNetFltWinPtUnloadProtocol() +{ + LogFlowFuncEnter(); + NDIS_STATUS Status = vboxNetFltWinPtDeregister(&g_VBoxNetFltGlobalsWin.Pt); + Assert(Status == NDIS_STATUS_SUCCESS); NOREF(Status); + LogFlowFunc(("LEAVE: PtDeregister Status (0x%x)\n", Status)); +} + + +static VOID vboxNetFltWinPtCloseAdapterComplete(IN NDIS_HANDLE ProtocolBindingContext, IN NDIS_STATUS Status) +{ + PVBOXNETFLTINS pNetFlt = (PVBOXNETFLTINS)ProtocolBindingContext; + + LogFlowFunc(("ENTER: pNetFlt (0x%p), Status (0x%x)\n", pNetFlt, Status)); + Assert(pNetFlt->u.s.WinIf.OpenCloseStatus == NDIS_STATUS_SUCCESS); + Assert(Status == NDIS_STATUS_SUCCESS); + Assert(pNetFlt->u.s.WinIf.OpenCloseStatus == NDIS_STATUS_SUCCESS); + if (pNetFlt->u.s.WinIf.OpenCloseStatus == NDIS_STATUS_SUCCESS) + { + pNetFlt->u.s.WinIf.OpenCloseStatus = Status; + } + NdisSetEvent(&pNetFlt->u.s.WinIf.OpenCloseEvent); + LogFlowFunc(("LEAVE: pNetFlt (0x%p), Status (0x%x)\n", pNetFlt, Status)); +} + +static VOID vboxNetFltWinPtResetComplete(IN NDIS_HANDLE hProtocolBindingContext, IN NDIS_STATUS Status) +{ + RT_NOREF2(hProtocolBindingContext, Status); + LogFlowFunc(("ENTER: pNetFlt 0x%p, Status 0x%x\n", hProtocolBindingContext, Status)); + /* + * should never be here + */ + AssertFailed(); + LogFlowFunc(("LEAVE: pNetFlt 0x%p, Status 0x%x\n", hProtocolBindingContext, Status)); +} + +static NDIS_STATUS vboxNetFltWinPtHandleQueryInfoComplete(PVBOXNETFLTINS pNetFlt, NDIS_STATUS Status) +{ + PNDIS_REQUEST pRequest = &pNetFlt->u.s.WinIf.PassDownRequest; + + switch (pRequest->DATA.QUERY_INFORMATION.Oid) + { + case OID_PNP_CAPABILITIES: + { + if (Status == NDIS_STATUS_SUCCESS) + { + if (pRequest->DATA.QUERY_INFORMATION.InformationBufferLength >= sizeof (NDIS_PNP_CAPABILITIES)) + { + PNDIS_PNP_CAPABILITIES pPnPCaps = (PNDIS_PNP_CAPABILITIES)(pRequest->DATA.QUERY_INFORMATION.InformationBuffer); + PNDIS_PM_WAKE_UP_CAPABILITIES pPmWuCaps = &pPnPCaps->WakeUpCapabilities; + pPmWuCaps->MinMagicPacketWakeUp = NdisDeviceStateUnspecified; + pPmWuCaps->MinPatternWakeUp = NdisDeviceStateUnspecified; + pPmWuCaps->MinLinkChangeWakeUp = NdisDeviceStateUnspecified; + *pNetFlt->u.s.WinIf.pcPDRBytesRW = sizeof (NDIS_PNP_CAPABILITIES); + *pNetFlt->u.s.WinIf.pcPDRBytesNeeded = 0; + Status = NDIS_STATUS_SUCCESS; + } + else + { + AssertFailed(); + *pNetFlt->u.s.WinIf.pcPDRBytesNeeded = sizeof(NDIS_PNP_CAPABILITIES); + Status = NDIS_STATUS_RESOURCES; + } + } + break; + } + + case OID_GEN_MAC_OPTIONS: + { + if (Status == NDIS_STATUS_SUCCESS) + { + if (pRequest->DATA.QUERY_INFORMATION.InformationBufferLength >= sizeof (ULONG)) + { + pNetFlt->u.s.WinIf.fMacOptions = *(PULONG)pRequest->DATA.QUERY_INFORMATION.InformationBuffer; +#ifndef VBOX_LOOPBACK_USEFLAGS + /* clearing this flag tells ndis we'll handle loopback ourselves + * the ndis layer or nic driver below us would loopback packets as necessary */ + *(PULONG)pRequest->DATA.QUERY_INFORMATION.InformationBuffer &= ~NDIS_MAC_OPTION_NO_LOOPBACK; +#else + /* we have to catch loopbacks from the underlying driver, so no duplications will occur, + * just indicate NDIS to handle loopbacks for the packets coming from the protocol */ + *(PULONG)pRequest->DATA.QUERY_INFORMATION.InformationBuffer |= NDIS_MAC_OPTION_NO_LOOPBACK; +#endif + } + else + { + AssertFailed(); + *pNetFlt->u.s.WinIf.pcPDRBytesNeeded = sizeof (ULONG); + Status = NDIS_STATUS_RESOURCES; + } + } + break; + } + + case OID_GEN_CURRENT_PACKET_FILTER: + { + if (VBOXNETFLT_PROMISCUOUS_SUPPORTED(pNetFlt)) + { + /* we're here _ONLY_ in the passthru mode */ + Assert(pNetFlt->u.s.WinIf.StateFlags.fProcessingPacketFilter && !pNetFlt->u.s.WinIf.StateFlags.fPPFNetFlt); + if (pNetFlt->u.s.WinIf.StateFlags.fProcessingPacketFilter && !pNetFlt->u.s.WinIf.StateFlags.fPPFNetFlt) + { + Assert(pNetFlt->enmTrunkState != INTNETTRUNKIFSTATE_ACTIVE); + vboxNetFltWinDereferenceModePassThru(pNetFlt); + vboxNetFltWinDereferenceWinIf(pNetFlt); + } + + if (Status == NDIS_STATUS_SUCCESS) + { + if (pRequest->DATA.QUERY_INFORMATION.InformationBufferLength >= sizeof (ULONG)) + { + /* the filter request is issued below only in case netflt is not active, + * simply update the cache here */ + /* cache the filter used by upper protocols */ + pNetFlt->u.s.WinIf.fUpperProtocolSetFilter = *(PULONG)pRequest->DATA.QUERY_INFORMATION.InformationBuffer; + pNetFlt->u.s.WinIf.StateFlags.fUpperProtSetFilterInitialized = TRUE; + } + else + { + AssertFailed(); + *pNetFlt->u.s.WinIf.pcPDRBytesNeeded = sizeof (ULONG); + Status = NDIS_STATUS_RESOURCES; + } + } + } + break; + } + + default: + Assert(pRequest->DATA.QUERY_INFORMATION.Oid != OID_PNP_QUERY_POWER); + break; + } + + *pNetFlt->u.s.WinIf.pcPDRBytesRW = pRequest->DATA.QUERY_INFORMATION.BytesWritten; + *pNetFlt->u.s.WinIf.pcPDRBytesNeeded = pRequest->DATA.QUERY_INFORMATION.BytesNeeded; + + return Status; +} + +static NDIS_STATUS vboxNetFltWinPtHandleSetInfoComplete(PVBOXNETFLTINS pNetFlt, NDIS_STATUS Status) +{ + PNDIS_REQUEST pRequest = &pNetFlt->u.s.WinIf.PassDownRequest; + + switch (pRequest->DATA.SET_INFORMATION.Oid) + { + case OID_GEN_CURRENT_PACKET_FILTER: + { + if (VBOXNETFLT_PROMISCUOUS_SUPPORTED(pNetFlt)) + { + Assert(Status == NDIS_STATUS_SUCCESS); + if (pNetFlt->u.s.WinIf.StateFlags.fProcessingPacketFilter) + { + if (pNetFlt->u.s.WinIf.StateFlags.fPPFNetFlt) + { + Assert(pNetFlt->enmTrunkState == INTNETTRUNKIFSTATE_ACTIVE); + pNetFlt->u.s.WinIf.StateFlags.fPPFNetFlt = 0; + if (Status == NDIS_STATUS_SUCCESS) + { + if (pRequest->DATA.SET_INFORMATION.InformationBufferLength >= sizeof (ULONG)) + { + pNetFlt->u.s.WinIf.fOurSetFilter = *((PULONG)pRequest->DATA.SET_INFORMATION.InformationBuffer); + Assert(pNetFlt->u.s.WinIf.fOurSetFilter == NDIS_PACKET_TYPE_PROMISCUOUS); + } + else + { + AssertFailed(); + *pNetFlt->u.s.WinIf.pcPDRBytesNeeded = sizeof (ULONG); + Status = NDIS_STATUS_RESOURCES; + } + } + vboxNetFltWinDereferenceNetFlt(pNetFlt); + } + else + { + Assert(pNetFlt->enmTrunkState != INTNETTRUNKIFSTATE_ACTIVE); + + if (Status == NDIS_STATUS_SUCCESS) + { + if (pRequest->DATA.SET_INFORMATION.InformationBufferLength >= sizeof (ULONG)) + { + /* the request was issued when the netflt was not active, simply update the cache here */ + pNetFlt->u.s.WinIf.fUpperProtocolSetFilter = *((PULONG)pRequest->DATA.SET_INFORMATION.InformationBuffer); + pNetFlt->u.s.WinIf.StateFlags.fUpperProtSetFilterInitialized = TRUE; + } + else + { + AssertFailed(); + *pNetFlt->u.s.WinIf.pcPDRBytesNeeded = sizeof (ULONG); + Status = NDIS_STATUS_RESOURCES; + } + } + vboxNetFltWinDereferenceModePassThru(pNetFlt); + } + + pNetFlt->u.s.WinIf.StateFlags.fProcessingPacketFilter = 0; + vboxNetFltWinDereferenceWinIf(pNetFlt); + } +#ifdef DEBUG_misha + else + { + AssertFailed(); + } +#endif + } + break; + } + + default: + Assert(pRequest->DATA.SET_INFORMATION.Oid != OID_PNP_SET_POWER); + break; + } + + *pNetFlt->u.s.WinIf.pcPDRBytesRW = pRequest->DATA.SET_INFORMATION.BytesRead; + *pNetFlt->u.s.WinIf.pcPDRBytesNeeded = pRequest->DATA.SET_INFORMATION.BytesNeeded; + + return Status; +} + +DECLHIDDEN(VOID) vboxNetFltWinPtRequestComplete(NDIS_HANDLE hContext, PNDIS_REQUEST pNdisRequest, NDIS_STATUS Status) +{ + PVBOXNETFLTINS pNetFlt = (PVBOXNETFLTINS)hContext; + PNDIS_REQUEST pSynchRequest = pNetFlt->u.s.WinIf.pSynchRequest; + + LogFlowFunc(("ENTER: pNetFlt (0x%p), pNdisRequest (0x%p), Status (0x%x)\n", pNetFlt, pNdisRequest, Status)); + + if (pSynchRequest == pNdisRequest) + { + /* asynchronous completion of our sync request */ + /*1.set the status */ + pNetFlt->u.s.WinIf.SynchCompletionStatus = Status; + /* 2. set event */ + KeSetEvent(&pNetFlt->u.s.WinIf.hSynchCompletionEvent, 0, FALSE); + /* 3. return; */ + + LogFlowFunc(("LEAVE: pNetFlt (0x%p), pNdisRequest (0x%p), Status (0x%x)\n", pNetFlt, pNdisRequest, Status)); + return; + } + + Assert(&pNetFlt->u.s.WinIf.PassDownRequest == pNdisRequest); + Assert(pNetFlt->u.s.WinIf.StateFlags.fRequestInfo == VBOXNDISREQUEST_INPROGRESS); + vboxNetFltWinMpRequestStateComplete(pNetFlt); + + switch (pNdisRequest->RequestType) + { + case NdisRequestQueryInformation: + Status = vboxNetFltWinPtHandleQueryInfoComplete(pNetFlt, Status); + NdisMQueryInformationComplete(pNetFlt->u.s.WinIf.hMiniport, Status); + break; + + case NdisRequestSetInformation: + Status = vboxNetFltWinPtHandleSetInfoComplete(pNetFlt, Status); + NdisMSetInformationComplete(pNetFlt->u.s.WinIf.hMiniport, Status); + break; + + default: + AssertFailed(); + break; + } + + LogFlowFunc(("LEAVE: pNetFlt (0x%p), pNdisRequest (0x%p), Status (0x%x)\n", pNetFlt, pNdisRequest, Status)); +} + +static VOID vboxNetFltWinPtStatus(IN NDIS_HANDLE hProtocolBindingContext, IN NDIS_STATUS GeneralStatus, IN PVOID pvStatusBuffer, IN UINT cbStatusBuffer) +{ + PVBOXNETFLTINS pNetFlt = (PVBOXNETFLTINS)hProtocolBindingContext; + + LogFlowFunc(("ENTER: pNetFlt (0x%p), GeneralStatus (0x%x)\n", pNetFlt, GeneralStatus)); + + if (vboxNetFltWinReferenceWinIf(pNetFlt)) + { + Assert(pNetFlt->u.s.WinIf.hMiniport); + + if (VBOXNETFLT_PT_STATUS_IS_FILTERED(GeneralStatus)) + { + pNetFlt->u.s.WinIf.MpIndicatedMediaStatus = GeneralStatus; + } + NdisMIndicateStatus(pNetFlt->u.s.WinIf.hMiniport, + GeneralStatus, + pvStatusBuffer, + cbStatusBuffer); + + vboxNetFltWinDereferenceWinIf(pNetFlt); + } + else + { + if (pNetFlt->u.s.WinIf.hMiniport != NULL + && VBOXNETFLT_PT_STATUS_IS_FILTERED(GeneralStatus) + ) + { + pNetFlt->u.s.WinIf.MpUnindicatedMediaStatus = GeneralStatus; + } + } + + LogFlowFunc(("LEAVE: pNetFlt (0x%p), GeneralStatus (0x%x)\n", pNetFlt, GeneralStatus)); +} + + +static VOID vboxNetFltWinPtStatusComplete(IN NDIS_HANDLE hProtocolBindingContext) +{ + PVBOXNETFLTINS pNetFlt = (PVBOXNETFLTINS)hProtocolBindingContext; + + LogFlowFunc(("ENTER: pNetFlt (0x%p)\n", pNetFlt)); + + if (vboxNetFltWinReferenceWinIf(pNetFlt)) + { + NdisMIndicateStatusComplete(pNetFlt->u.s.WinIf.hMiniport); + + vboxNetFltWinDereferenceWinIf(pNetFlt); + } + + LogFlowFunc(("LEAVE: pNetFlt (0x%p)\n", pNetFlt)); +} + +static VOID vboxNetFltWinPtSendComplete(IN NDIS_HANDLE hProtocolBindingContext, IN PNDIS_PACKET pPacket, IN NDIS_STATUS Status) +{ + PVBOXNETFLTINS pNetFlt = (PVBOXNETFLTINS)hProtocolBindingContext; + PVBOXNETFLT_PKTRSVD_PT pSendInfo = (PVBOXNETFLT_PKTRSVD_PT)pPacket->ProtocolReserved; + PNDIS_PACKET pOrigPacket = pSendInfo->pOrigPacket; + PVOID pBufToFree = pSendInfo->pBufToFree; + LogFlowFunc(("ENTER: pNetFlt (0x%p), pPacket (0x%p), Status (0x%x)\n", pNetFlt, pPacket, Status)); + +#if defined(DEBUG_NETFLT_PACKETS) || !defined(VBOX_LOOPBACK_USEFLAGS) + /** @todo for optimization we could check only for netflt-mode packets + * do it for all for now */ + vboxNetFltWinLbRemoveSendPacket(pNetFlt, pPacket); +#endif + + if (pOrigPacket) + { + NdisIMCopySendCompletePerPacketInfo(pOrigPacket, pPacket); + NdisFreePacket(pPacket); + /* the ptk was posted from the upperlying protocol */ + NdisMSendComplete(pNetFlt->u.s.WinIf.hMiniport, pOrigPacket, Status); + } + else + { + /* if the pOrigPacket is zero - the ptk was originated by netFlt send/receive + * need to free packet buffers */ + vboxNetFltWinFreeSGNdisPacket(pPacket, !pBufToFree); + } + + if (pBufToFree) + { + vboxNetFltWinMemFree(pBufToFree); + } + + vboxNetFltWinDereferenceWinIf(pNetFlt); + + LogFlowFunc(("LEAVE: pNetFlt (0x%p), pPacket (0x%p), Status (0x%x)\n", pNetFlt, pPacket, Status)); +} + +/** + * removes searches for the packet in the list and removes it if found + * @return true if the packet was found and removed, false - otherwise + */ +static bool vboxNetFltWinRemovePacketFromList(PVBOXNETFLT_INTERLOCKED_SINGLE_LIST pList, PNDIS_PACKET pPacket) +{ + PVBOXNETFLT_PKTRSVD_TRANSFERDATA_PT pTDR = (PVBOXNETFLT_PKTRSVD_TRANSFERDATA_PT)pPacket->ProtocolReserved; + return vboxNetFltWinInterlockedSearchListEntry(pList, &pTDR->ListEntry, true /* remove*/); +} + +/** + * puts the packet to the tail of the list + */ +static void vboxNetFltWinPutPacketToList(PVBOXNETFLT_INTERLOCKED_SINGLE_LIST pList, PNDIS_PACKET pPacket, PNDIS_BUFFER pOrigBuffer) +{ + PVBOXNETFLT_PKTRSVD_TRANSFERDATA_PT pTDR = (PVBOXNETFLT_PKTRSVD_TRANSFERDATA_PT)pPacket->ProtocolReserved; + pTDR->pOrigBuffer = pOrigBuffer; + vboxNetFltWinInterlockedPutTail(pList, &pTDR->ListEntry); +} + +static bool vboxNetFltWinPtTransferDataCompleteActive(PVBOXNETFLTINS pNetFltIf, PNDIS_PACKET pPacket, NDIS_STATUS Status) +{ + PNDIS_BUFFER pBuffer; + PVBOXNETFLT_PKTRSVD_TRANSFERDATA_PT pTDR; + + if (!vboxNetFltWinRemovePacketFromList(&pNetFltIf->u.s.WinIf.TransferDataList, pPacket)) + return false; + + pTDR = (PVBOXNETFLT_PKTRSVD_TRANSFERDATA_PT)pPacket->ProtocolReserved; + Assert(pTDR); + Assert(pTDR->pOrigBuffer); + + do + { + NdisUnchainBufferAtFront(pPacket, &pBuffer); + + Assert(pBuffer); + + NdisFreeBuffer(pBuffer); + + pBuffer = pTDR->pOrigBuffer; + + NdisChainBufferAtBack(pPacket, pBuffer); + + /* data transfer was initiated when the netFlt was active + * the netFlt is still retained by us + * 1. check if loopback + * 2. enqueue packet + * 3. release netFlt */ + + if (Status == NDIS_STATUS_SUCCESS) + { + +#ifdef VBOX_LOOPBACK_USEFLAGS + if (vboxNetFltWinIsLoopedBackPacket(pPacket)) + { + /* should not be here */ + AssertFailed(); + } +#else + PNDIS_PACKET pLb = vboxNetFltWinLbSearchLoopBack(pNetFltIf, pPacket, false); + if (pLb) + { +#ifndef DEBUG_NETFLT_RECV_TRANSFERDATA + /* should not be here */ + AssertFailed(); +#endif + if (!vboxNetFltWinLbIsFromIntNet(pLb)) + { + /* the packet is not from int net, need to pass it up to the host */ + NdisMIndicateReceivePacket(pNetFltIf->u.s.WinIf.hMiniport, &pPacket, 1); + /* dereference NetFlt, WinIf will be dereferenced on Packet return */ + vboxNetFltWinDereferenceNetFlt(pNetFltIf); + break; + } + } +#endif + else + { + /* 2. enqueue */ + /* use the same packet info to put the packet in the processing packet queue */ + PVBOXNETFLT_PKTRSVD_MP pRecvInfo = (PVBOXNETFLT_PKTRSVD_MP)pPacket->MiniportReserved; + + VBOXNETFLT_LBVERIFY(pNetFltIf, pPacket); + + pRecvInfo->pOrigPacket = NULL; + pRecvInfo->pBufToFree = NULL; + + NdisGetPacketFlags(pPacket) = 0; +# ifdef VBOXNETFLT_NO_PACKET_QUEUE + if (vboxNetFltWinPostIntnet(pNetFltIf, pPacket, 0)) + { + /* drop it */ + vboxNetFltWinFreeSGNdisPacket(pPacket, true); + vboxNetFltWinDereferenceWinIf(pNetFltIf); + } + else + { + NdisMIndicateReceivePacket(pNetFltIf->u.s.WinIf.hMiniport, &pPacket, 1); + } + vboxNetFltWinDereferenceNetFlt(pNetFltIf); + break; +# else + Status = vboxNetFltWinQuEnqueuePacket(pNetFltIf, pPacket, PACKET_MINE); + if (Status == NDIS_STATUS_SUCCESS) + { + break; + } + AssertFailed(); +# endif + } + } + else + { + AssertFailed(); + } + /* we are here because of error either in data transfer or in enqueueing the packet */ + vboxNetFltWinFreeSGNdisPacket(pPacket, true); + vboxNetFltWinDereferenceNetFlt(pNetFltIf); + vboxNetFltWinDereferenceWinIf(pNetFltIf); + } while (0); + + return true; +} + +static VOID vboxNetFltWinPtTransferDataComplete(IN NDIS_HANDLE hProtocolBindingContext, + IN PNDIS_PACKET pPacket, + IN NDIS_STATUS Status, + IN UINT cbTransferred) +{ + PVBOXNETFLTINS pNetFlt = (PVBOXNETFLTINS)hProtocolBindingContext; + LogFlowFunc(("ENTER: pNetFlt (0x%p), pPacket (0x%p), Status (0x%x), cbTransfered (%d)\n", pNetFlt, pPacket, Status, cbTransferred)); + if (!vboxNetFltWinPtTransferDataCompleteActive(pNetFlt, pPacket, Status)) + { + if (pNetFlt->u.s.WinIf.hMiniport) + { + NdisMTransferDataComplete(pNetFlt->u.s.WinIf.hMiniport, + pPacket, + Status, + cbTransferred); + } + + vboxNetFltWinDereferenceWinIf(pNetFlt); + } + /* else - all processing is done with vboxNetFltWinPtTransferDataCompleteActive already */ + + LogFlowFunc(("LEAVE: pNetFlt (0x%p), pPacket (0x%p), Status (0x%x), cbTransfered (%d)\n", pNetFlt, pPacket, Status, cbTransferred)); +} + +static INT vboxNetFltWinRecvPacketPassThru(PVBOXNETFLTINS pNetFlt, PNDIS_PACKET pPacket) +{ + Assert(KeGetCurrentIrql() == DISPATCH_LEVEL); + + PNDIS_PACKET pMyPacket; + NDIS_STATUS Status = vboxNetFltWinPrepareRecvPacket(pNetFlt, pPacket, &pMyPacket, true); + /* the Status holds the current packet status it will be checked for NDIS_STATUS_RESOURCES later + * (see below) */ + Assert(pMyPacket); + if (pMyPacket) + { + NdisMIndicateReceivePacket(pNetFlt->u.s.WinIf.hMiniport, &pMyPacket, 1); + if (Status == NDIS_STATUS_RESOURCES) + { + NdisDprFreePacket(pMyPacket); + return 0; + } + + return 1; + } + + return 0; +} + +/** + * process the packet receive in a "passthru" mode + */ +static NDIS_STATUS vboxNetFltWinRecvPassThru(PVBOXNETFLTINS pNetFlt, PNDIS_PACKET pPacket) +{ + Assert(KeGetCurrentIrql() == DISPATCH_LEVEL); + + NDIS_STATUS Status; + PNDIS_PACKET pMyPacket; + + NdisDprAllocatePacket(&Status, &pMyPacket, pNetFlt->u.s.WinIf.hRecvPacketPool); + Assert(Status == NDIS_STATUS_SUCCESS); + if (Status == NDIS_STATUS_SUCCESS) + { + vboxNetFltWinCopyPacketInfoOnRecv(pMyPacket, pPacket, true /* force NDIS_STATUS_RESOURCES */); + Assert(NDIS_GET_PACKET_STATUS(pMyPacket) == NDIS_STATUS_RESOURCES); + + NdisMIndicateReceivePacket(pNetFlt->u.s.WinIf.hMiniport, &pMyPacket, 1); + + NdisDprFreePacket(pMyPacket); + } + return Status; +} + +static VOID vboxNetFltWinRecvIndicatePassThru(PVBOXNETFLTINS pNetFlt, NDIS_HANDLE MacReceiveContext, + PVOID pHeaderBuffer, UINT cbHeaderBuffer, PVOID pLookAheadBuffer, UINT cbLookAheadBuffer, UINT cbPacket) +{ + /* Note: we're using KeGetCurrentProcessorNumber, which is not entirely correct in case + * we're running on 64bit win7+, which can handle > 64 CPUs, however since KeGetCurrentProcessorNumber + * always returns the number < than the number of CPUs in the first group, we're guaranteed to have CPU index < 64 + * @todo: use KeGetCurrentProcessorNumberEx for Win7+ 64 and dynamically extended array */ + ULONG Proc = KeGetCurrentProcessorNumber(); + Assert(Proc < RT_ELEMENTS(pNetFlt->u.s.WinIf.abIndicateRxComplete)); + pNetFlt->u.s.WinIf.abIndicateRxComplete[Proc] = TRUE; + switch (pNetFlt->u.s.WinIf.enmMedium) + { + case NdisMedium802_3: + case NdisMediumWan: + NdisMEthIndicateReceive(pNetFlt->u.s.WinIf.hMiniport, + MacReceiveContext, + (PCHAR)pHeaderBuffer, + cbHeaderBuffer, + pLookAheadBuffer, + cbLookAheadBuffer, + cbPacket); + break; + default: + AssertFailed(); + break; + } +} + +/** + * process the ProtocolReceive in an "active" mode + * + * @return NDIS_STATUS_SUCCESS - the packet is processed + * NDIS_STATUS_PENDING - the packet is being processed, we are waiting for the ProtocolTransferDataComplete to be called + * NDIS_STATUS_NOT_ACCEPTED - the packet is not needed - typically this is because this is a loopback packet + * NDIS_STATUS_FAILURE - packet processing failed + */ +static NDIS_STATUS vboxNetFltWinPtReceiveActive(PVBOXNETFLTINS pNetFlt, NDIS_HANDLE MacReceiveContext, PVOID pHeaderBuffer, UINT cbHeaderBuffer, + PVOID pLookaheadBuffer, UINT cbLookaheadBuffer, UINT cbPacket) +{ + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + + do + { + if (cbHeaderBuffer != VBOXNETFLT_PACKET_ETHEADER_SIZE) + { + Status = NDIS_STATUS_NOT_ACCEPTED; + break; + } + +#ifndef DEBUG_NETFLT_RECV_TRANSFERDATA + if (cbPacket == cbLookaheadBuffer) + { + PINTNETSG pSG; + PUCHAR pRcvData; +#ifndef VBOX_LOOPBACK_USEFLAGS + PNDIS_PACKET pLb; +#endif + + /* allocate SG buffer */ + Status = vboxNetFltWinAllocSG(cbPacket + cbHeaderBuffer, &pSG); + if (Status != NDIS_STATUS_SUCCESS) + { + AssertFailed(); + break; + } + + pRcvData = (PUCHAR)pSG->aSegs[0].pv; + + NdisMoveMappedMemory(pRcvData, pHeaderBuffer, cbHeaderBuffer); + + NdisCopyLookaheadData(pRcvData+cbHeaderBuffer, + pLookaheadBuffer, + cbLookaheadBuffer, + pNetFlt->u.s.WinIf.fMacOptions); +#ifndef VBOX_LOOPBACK_USEFLAGS + pLb = vboxNetFltWinLbSearchLoopBackBySG(pNetFlt, pSG, false); + if (pLb) + { +#ifndef DEBUG_NETFLT_RECV_NOPACKET + /* should not be here */ + AssertFailed(); +#endif + if (!vboxNetFltWinLbIsFromIntNet(pLb)) + { + PNDIS_PACKET pMyPacket; + pMyPacket = vboxNetFltWinNdisPacketFromSG(pNetFlt, /* PVBOXNETFLTINS */ + pSG, /* PINTNETSG */ + pSG, /* PVOID pBufToFree */ + false, /* bool bToWire */ + false); /* bool bCopyMemory */ + if (pMyPacket) + { + NdisMIndicateReceivePacket(pNetFlt->u.s.WinIf.hMiniport, &pMyPacket, 1); + /* dereference the NetFlt here & indicate SUCCESS, which would mean the caller would not do a dereference + * the WinIf dereference will be done on packet return */ + vboxNetFltWinDereferenceNetFlt(pNetFlt); + Status = NDIS_STATUS_SUCCESS; + } + else + { + vboxNetFltWinMemFree(pSG); + Status = NDIS_STATUS_FAILURE; + } + } + else + { + vboxNetFltWinMemFree(pSG); + Status = NDIS_STATUS_NOT_ACCEPTED; + } + break; + } +#endif + VBOXNETFLT_LBVERIFYSG(pNetFlt, pSG); + + /* enqueue SG */ +# ifdef VBOXNETFLT_NO_PACKET_QUEUE + if (vboxNetFltWinPostIntnet(pNetFlt, pSG, VBOXNETFLT_PACKET_SG)) + { + /* drop it */ + vboxNetFltWinMemFree(pSG); + vboxNetFltWinDereferenceWinIf(pNetFlt); + } + else + { + PNDIS_PACKET pMyPacket = vboxNetFltWinNdisPacketFromSG(pNetFlt, /* PVBOXNETFLTINS */ + pSG, /* PINTNETSG */ + pSG, /* PVOID pBufToFree */ + false, /* bool bToWire */ + false); /* bool bCopyMemory */ + Assert(pMyPacket); + if (pMyPacket) + { + NDIS_SET_PACKET_STATUS(pMyPacket, NDIS_STATUS_SUCCESS); + + DBG_CHECK_PACKET_AND_SG(pMyPacket, pSG); + + LogFlow(("non-ndis packet info, packet created (%p)\n", pMyPacket)); + NdisMIndicateReceivePacket(pNetFlt->u.s.WinIf.hMiniport, &pMyPacket, 1); + } + else + { + vboxNetFltWinDereferenceWinIf(pNetFlt); + Status = NDIS_STATUS_RESOURCES; + } + } + vboxNetFltWinDereferenceNetFlt(pNetFlt); +# else + Status = vboxNetFltWinQuEnqueuePacket(pNetFlt, pSG, PACKET_SG | PACKET_MINE); + if (Status != NDIS_STATUS_SUCCESS) + { + AssertFailed(); + vboxNetFltWinMemFree(pSG); + break; + } +# endif +#endif + } + else + { + PNDIS_PACKET pPacket; + PNDIS_BUFFER pTransferBuffer; + PNDIS_BUFFER pOrigBuffer; + PUCHAR pMemBuf; + UINT cbBuf = cbPacket + cbHeaderBuffer; + UINT cbTransferred; + + /* allocate NDIS Packet buffer */ + NdisAllocatePacket(&Status, &pPacket, pNetFlt->u.s.WinIf.hRecvPacketPool); + if (Status != NDIS_STATUS_SUCCESS) + { + AssertFailed(); + break; + } + + VBOXNETFLT_OOB_INIT(pPacket); + +#ifdef VBOX_LOOPBACK_USEFLAGS + /* set "don't loopback" flags */ + NdisGetPacketFlags(pPacket) = g_VBoxNetFltGlobalsWin.fPacketDontLoopBack; +#else + NdisGetPacketFlags(pPacket) = 0; +#endif + + Status = vboxNetFltWinMemAlloc((PVOID*)(&pMemBuf), cbBuf); + if (Status != NDIS_STATUS_SUCCESS) + { + AssertFailed(); + NdisFreePacket(pPacket); + break; + } + NdisAllocateBuffer(&Status, &pTransferBuffer, pNetFlt->u.s.WinIf.hRecvBufferPool, pMemBuf + cbHeaderBuffer, cbPacket); + if (Status != NDIS_STATUS_SUCCESS) + { + AssertFailed(); + Status = NDIS_STATUS_FAILURE; + NdisFreePacket(pPacket); + vboxNetFltWinMemFree(pMemBuf); + break; + } + + NdisAllocateBuffer(&Status, &pOrigBuffer, pNetFlt->u.s.WinIf.hRecvBufferPool, pMemBuf, cbBuf); + if (Status != NDIS_STATUS_SUCCESS) + { + AssertFailed(); + Status = NDIS_STATUS_FAILURE; + NdisFreeBuffer(pTransferBuffer); + NdisFreePacket(pPacket); + vboxNetFltWinMemFree(pMemBuf); + break; + } + + NdisChainBufferAtBack(pPacket, pTransferBuffer); + + NdisMoveMappedMemory(pMemBuf, pHeaderBuffer, cbHeaderBuffer); + + vboxNetFltWinPutPacketToList(&pNetFlt->u.s.WinIf.TransferDataList, pPacket, pOrigBuffer); + +#ifdef DEBUG_NETFLT_RECV_TRANSFERDATA + if (cbPacket == cbLookaheadBuffer) + { + NdisCopyLookaheadData(pMemBuf+cbHeaderBuffer, + pLookaheadBuffer, + cbLookaheadBuffer, + pNetFlt->u.s.WinIf.fMacOptions); + } + else +#endif + { + Assert(cbPacket > cbLookaheadBuffer); + + NdisTransferData(&Status, pNetFlt->u.s.WinIf.hBinding, MacReceiveContext, + 0, /* ByteOffset */ + cbPacket, pPacket, &cbTransferred); + } + + if (Status != NDIS_STATUS_PENDING) + { + vboxNetFltWinPtTransferDataComplete(pNetFlt, pPacket, Status, cbTransferred); + } + } + } while (0); + + return Status; +} + +static NDIS_STATUS vboxNetFltWinPtReceive(IN NDIS_HANDLE hProtocolBindingContext, + IN NDIS_HANDLE MacReceiveContext, + IN PVOID pHeaderBuffer, + IN UINT cbHeaderBuffer, + IN PVOID pLookAheadBuffer, + IN UINT cbLookAheadBuffer, + IN UINT cbPacket) +{ + PVBOXNETFLTINS pNetFlt = (PVBOXNETFLTINS)hProtocolBindingContext; + PNDIS_PACKET pPacket = NULL; + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + bool bNetFltActive; + bool fWinIfActive = vboxNetFltWinReferenceWinIfNetFlt(pNetFlt, &bNetFltActive); + const bool bPassThruActive = !bNetFltActive; + + LogFlowFunc(("ENTER: pNetFlt (0x%p)\n", pNetFlt)); + + if (fWinIfActive) + { + do + { +#ifndef DEBUG_NETFLT_RECV_NOPACKET + pPacket = NdisGetReceivedPacket(pNetFlt->u.s.WinIf.hBinding, MacReceiveContext); + if (pPacket) + { +# ifndef VBOX_LOOPBACK_USEFLAGS + PNDIS_PACKET pLb = NULL; +# else + if (vboxNetFltWinIsLoopedBackPacket(pPacket)) + { + AssertFailed(); + /* nothing else to do here, just return the packet */ + //NdisReturnPackets(&pPacket, 1); + Status = NDIS_STATUS_NOT_ACCEPTED; + break; + } + + VBOXNETFLT_LBVERIFY(pNetFlt, pPacket); +# endif + + if (bNetFltActive) + { +# ifndef VBOX_LOOPBACK_USEFLAGS + pLb = vboxNetFltWinLbSearchLoopBack(pNetFlt, pPacket, false); + if (!pLb) +# endif + { + VBOXNETFLT_LBVERIFY(pNetFlt, pPacket); + +# ifdef VBOXNETFLT_NO_PACKET_QUEUE + if (vboxNetFltWinPostIntnet(pNetFlt, pPacket, 0)) + { + /* drop it */ + break; + } +# else + Status = vboxNetFltWinQuEnqueuePacket(pNetFlt, pPacket, PACKET_COPY); + Assert(Status == NDIS_STATUS_SUCCESS); + if (Status == NDIS_STATUS_SUCCESS) + { + //NdisReturnPackets(&pPacket, 1); + fWinIfActive = false; + bNetFltActive = false; + break; + } +# endif + } +# ifndef VBOX_LOOPBACK_USEFLAGS + else if (vboxNetFltWinLbIsFromIntNet(pLb)) + { + /* nothing else to do here, just return the packet */ + //NdisReturnPackets(&pPacket, 1); + Status = NDIS_STATUS_NOT_ACCEPTED; + break; + } + /* we are here because this is a looped back packet set not from intnet + * we will post it to the upper protocol */ +# endif + } + + Assert(Status == STATUS_SUCCESS); + if (Status == STATUS_SUCCESS) + { +# ifndef VBOX_LOOPBACK_USEFLAGS + Assert(!pLb || !vboxNetFltWinLbIsFromIntNet(pLb)); +# endif + Status = vboxNetFltWinRecvPassThru(pNetFlt, pPacket); + Assert(Status == STATUS_SUCCESS); + /* we are done with packet processing, and we will + * not receive packet return event for this packet, + * fWinIfActive should be true to ensure we release WinIf*/ + Assert(fWinIfActive); + if (Status == STATUS_SUCCESS) + break; + } + else + { + /* intnet processing failed - fall back to no-packet mode */ + Assert(bNetFltActive); + Assert(fWinIfActive); + } + + } +#endif /* #ifndef DEBUG_NETFLT_RECV_NOPACKET */ + + if (bNetFltActive) + { + Status = vboxNetFltWinPtReceiveActive(pNetFlt, MacReceiveContext, pHeaderBuffer, cbHeaderBuffer, + pLookAheadBuffer, cbLookAheadBuffer, cbPacket); + if (NT_SUCCESS(Status)) + { + if (Status != NDIS_STATUS_NOT_ACCEPTED) + { + fWinIfActive = false; + bNetFltActive = false; + } + else + { +#ifndef VBOX_LOOPBACK_USEFLAGS + /* this is a loopback packet, nothing to do here */ +#else + AssertFailed(); + /* should not be here */ +#endif + } + break; + } + } + + /* we are done with packet processing, and we will + * not receive packet return event for this packet, + * fWinIfActive should be true to ensure we release WinIf*/ + Assert(fWinIfActive); + + vboxNetFltWinRecvIndicatePassThru(pNetFlt, MacReceiveContext, pHeaderBuffer, cbHeaderBuffer, pLookAheadBuffer, cbLookAheadBuffer, cbPacket); + /* the status could contain an error value here in case the IntNet recv failed, + * ensure we return back success status */ + Status = NDIS_STATUS_SUCCESS; + + } while (0); + + if (bNetFltActive) + { + vboxNetFltWinDereferenceNetFlt(pNetFlt); + } + else if (bPassThruActive) + { + vboxNetFltWinDereferenceModePassThru(pNetFlt); + } + if (fWinIfActive) + { + vboxNetFltWinDereferenceWinIf(pNetFlt); + } + } + else + { + Status = NDIS_STATUS_FAILURE; + } + + LogFlowFunc(("LEAVE: pNetFlt (0x%p)\n", pNetFlt)); + + return Status; + +} + +static VOID vboxNetFltWinPtReceiveComplete(NDIS_HANDLE hProtocolBindingContext) +{ + PVBOXNETFLTINS pNetFlt = (PVBOXNETFLTINS)hProtocolBindingContext; + bool fNetFltActive; + bool fWinIfActive = vboxNetFltWinReferenceWinIfNetFlt(pNetFlt, &fNetFltActive); + NDIS_HANDLE hMiniport = pNetFlt->u.s.WinIf.hMiniport; + /* Note: we're using KeGetCurrentProcessorNumber, which is not entirely correct in case + * we're running on 64bit win7+, which can handle > 64 CPUs, however since KeGetCurrentProcessorNumber + * always returns the number < than the number of CPUs in the first group, we're guaranteed to have CPU index < 64 + * @todo: use KeGetCurrentProcessorNumberEx for Win7+ 64 and dynamically extended array */ + ULONG iProc = KeGetCurrentProcessorNumber(); + Assert(iProc < RT_ELEMENTS(pNetFlt->u.s.WinIf.abIndicateRxComplete)); + + LogFlowFunc(("ENTER: pNetFlt (0x%p)\n", pNetFlt)); + + if (hMiniport != NULL && pNetFlt->u.s.WinIf.abIndicateRxComplete[iProc]) + { + switch (pNetFlt->u.s.WinIf.enmMedium) + { + case NdisMedium802_3: + case NdisMediumWan: + NdisMEthIndicateReceiveComplete(hMiniport); + break; + default: + AssertFailed(); + break; + } + } + + pNetFlt->u.s.WinIf.abIndicateRxComplete[iProc] = FALSE; + + if (fWinIfActive) + { + if (fNetFltActive) + vboxNetFltWinDereferenceNetFlt(pNetFlt); + else + vboxNetFltWinDereferenceModePassThru(pNetFlt); + vboxNetFltWinDereferenceWinIf(pNetFlt); + } + + LogFlowFunc(("LEAVE: pNetFlt (0x%p)\n", pNetFlt)); +} + +static INT vboxNetFltWinPtReceivePacket(NDIS_HANDLE hProtocolBindingContext, PNDIS_PACKET pPacket) +{ + PVBOXNETFLTINS pNetFlt = (PVBOXNETFLTINS)hProtocolBindingContext; + INT cRefCount = 0; + bool bNetFltActive; + bool fWinIfActive = vboxNetFltWinReferenceWinIfNetFlt(pNetFlt, &bNetFltActive); + const bool bPassThruActive = !bNetFltActive; + + LogFlowFunc(("ENTER: pNetFlt (0x%p)\n", pNetFlt)); + + if (fWinIfActive) + { + do + { +#ifdef VBOX_LOOPBACK_USEFLAGS + if (vboxNetFltWinIsLoopedBackPacket(pPacket)) + { + AssertFailed(); + Log(("lb_rp")); + + /* nothing else to do here, just return the packet */ + cRefCount = 0; + //NdisReturnPackets(&pPacket, 1); + break; + } + + VBOXNETFLT_LBVERIFY(pNetFlt, pPacket); +#endif + + if (bNetFltActive) + { +#ifndef VBOX_LOOPBACK_USEFLAGS + PNDIS_PACKET pLb = vboxNetFltWinLbSearchLoopBack(pNetFlt, pPacket, false); + if (!pLb) +#endif + { +#ifndef VBOXNETFLT_NO_PACKET_QUEUE + NDIS_STATUS fStatus; +#endif + bool fResources = NDIS_GET_PACKET_STATUS(pPacket) == NDIS_STATUS_RESOURCES; NOREF(fResources); + + VBOXNETFLT_LBVERIFY(pNetFlt, pPacket); +#ifdef DEBUG_misha + /** @todo remove this assert. + * this is a temporary assert for debugging purposes: + * we're probably doing something wrong with the packets if the miniport reports NDIS_STATUS_RESOURCES */ + Assert(!fResources); +#endif + +#ifdef VBOXNETFLT_NO_PACKET_QUEUE + if (vboxNetFltWinPostIntnet(pNetFlt, pPacket, 0)) + { + /* drop it */ + cRefCount = 0; + break; + } + +#else + fStatus = vboxNetFltWinQuEnqueuePacket(pNetFlt, pPacket, fResources ? PACKET_COPY : 0); + if (fStatus == NDIS_STATUS_SUCCESS) + { + bNetFltActive = false; + fWinIfActive = false; + if (fResources) + { + cRefCount = 0; + //NdisReturnPackets(&pPacket, 1); + } + else + cRefCount = 1; + break; + } + else + { + AssertFailed(); + } +#endif + } +#ifndef VBOX_LOOPBACK_USEFLAGS + else if (vboxNetFltWinLbIsFromIntNet(pLb)) + { + /* the packet is from intnet, it has already been set to the host, + * no need for loopng it back to the host again */ + /* nothing else to do here, just return the packet */ + cRefCount = 0; + //NdisReturnPackets(&pPacket, 1); + break; + } +#endif + } + + cRefCount = vboxNetFltWinRecvPacketPassThru(pNetFlt, pPacket); + if (cRefCount) + { + Assert(cRefCount == 1); + fWinIfActive = false; + } + + } while (FALSE); + + if (bNetFltActive) + { + vboxNetFltWinDereferenceNetFlt(pNetFlt); + } + else if (bPassThruActive) + { + vboxNetFltWinDereferenceModePassThru(pNetFlt); + } + if (fWinIfActive) + { + vboxNetFltWinDereferenceWinIf(pNetFlt); + } + } + else + { + cRefCount = 0; + //NdisReturnPackets(&pPacket, 1); + } + + LogFlowFunc(("LEAVE: pNetFlt (0x%p), cRefCount (%d)\n", pNetFlt, cRefCount)); + + return cRefCount; +} + +DECLHIDDEN(bool) vboxNetFltWinPtCloseInterface(PVBOXNETFLTINS pNetFlt, PNDIS_STATUS pStatus) +{ + RTSpinlockAcquire(pNetFlt->hSpinlock); + + if (pNetFlt->u.s.WinIf.StateFlags.fInterfaceClosing) + { + RTSpinlockRelease(pNetFlt->hSpinlock); + AssertFailed(); + return false; + } + if (pNetFlt->u.s.WinIf.hBinding == NULL) + { + RTSpinlockRelease(pNetFlt->hSpinlock); + AssertFailed(); + return false; + } + + pNetFlt->u.s.WinIf.StateFlags.fInterfaceClosing = TRUE; + RTSpinlockRelease(pNetFlt->hSpinlock); + + NdisResetEvent(&pNetFlt->u.s.WinIf.OpenCloseEvent); + NdisCloseAdapter(pStatus, pNetFlt->u.s.WinIf.hBinding); + if (*pStatus == NDIS_STATUS_PENDING) + { + NdisWaitEvent(&pNetFlt->u.s.WinIf.OpenCloseEvent, 0); + *pStatus = pNetFlt->u.s.WinIf.OpenCloseStatus; + } + + Assert (*pStatus == NDIS_STATUS_SUCCESS); + + pNetFlt->u.s.WinIf.hBinding = NULL; + + return true; +} + +static NDIS_STATUS vboxNetFltWinPtPnPSetPower(PVBOXNETFLTINS pNetFlt, NDIS_DEVICE_POWER_STATE enmPowerState) +{ + NDIS_DEVICE_POWER_STATE enmPrevPowerState = vboxNetFltWinGetPowerState(&pNetFlt->u.s.WinIf.PtState); + + RTSpinlockAcquire(pNetFlt->hSpinlock); + + vboxNetFltWinSetPowerState(&pNetFlt->u.s.WinIf.PtState, enmPowerState); + + if (vboxNetFltWinGetPowerState(&pNetFlt->u.s.WinIf.PtState) > NdisDeviceStateD0) + { + if (enmPrevPowerState == NdisDeviceStateD0) + { + pNetFlt->u.s.WinIf.StateFlags.fStandBy = TRUE; + } + RTSpinlockRelease(pNetFlt->hSpinlock); + vboxNetFltWinPtRequestsWaitComplete(pNetFlt); + vboxNetFltWinWaitDereference(&pNetFlt->u.s.WinIf.MpState); + vboxNetFltWinWaitDereference(&pNetFlt->u.s.WinIf.PtState); + + /* check packet pool is empty */ + UINT cPPUsage = NdisPacketPoolUsage(pNetFlt->u.s.WinIf.hSendPacketPool); + Assert(cPPUsage == 0); + cPPUsage = NdisPacketPoolUsage(pNetFlt->u.s.WinIf.hRecvPacketPool); + Assert(cPPUsage == 0); + /* for debugging only, ignore the err in release */ + NOREF(cPPUsage); + + Assert(!pNetFlt->u.s.WinIf.StateFlags.fRequestInfo); + } + else + { + if (enmPrevPowerState > NdisDeviceStateD0) + { + pNetFlt->u.s.WinIf.StateFlags.fStandBy = FALSE; + } + + if (pNetFlt->u.s.WinIf.StateFlags.fRequestInfo & VBOXNDISREQUEST_QUEUED) + { + pNetFlt->u.s.WinIf.StateFlags.fRequestInfo = VBOXNDISREQUEST_INPROGRESS; + RTSpinlockRelease(pNetFlt->hSpinlock); + + vboxNetFltWinMpRequestPost(pNetFlt); + } + else + { + RTSpinlockRelease(pNetFlt->hSpinlock); + } + } + + return NDIS_STATUS_SUCCESS; +} + + +static NDIS_STATUS vboxNetFltWinPtPnPEvent(IN NDIS_HANDLE hProtocolBindingContext, IN PNET_PNP_EVENT pNetPnPEvent) +{ + PVBOXNETFLTINS pNetFlt = (PVBOXNETFLTINS)hProtocolBindingContext; + + LogFlowFunc(("ENTER: pNetFlt (0x%p), NetEvent (%d)\n", pNetFlt, pNetPnPEvent->NetEvent)); + + switch (pNetPnPEvent->NetEvent) + { + case NetEventSetPower: + { + NDIS_DEVICE_POWER_STATE enmPowerState = *((PNDIS_DEVICE_POWER_STATE)pNetPnPEvent->Buffer); + NDIS_STATUS rcNdis = vboxNetFltWinPtPnPSetPower(pNetFlt, enmPowerState); + LogFlowFunc(("LEAVE: pNetFlt (0x%p), NetEvent (%d), rcNdis=%#x\n", pNetFlt, pNetPnPEvent->NetEvent, rcNdis)); + return rcNdis; + } + + case NetEventReconfigure: + { + if (!pNetFlt) + { + NdisReEnumerateProtocolBindings(g_VBoxNetFltGlobalsWin.Pt.hProtocol); + } + } + /** @todo r=bird: Is the fall thru intentional?? */ + default: + LogFlowFunc(("LEAVE: pNetFlt (0x%p), NetEvent (%d)\n", pNetFlt, pNetPnPEvent->NetEvent)); + return NDIS_STATUS_SUCCESS; + } + +} + +#ifdef __cplusplus +# define PTCHARS_40(_p) ((_p).Ndis40Chars) +#else +# define PTCHARS_40(_p) (_p) +#endif + +/** + * register the protocol edge + */ +DECLHIDDEN(NDIS_STATUS) vboxNetFltWinPtRegister(PVBOXNETFLTGLOBALS_PT pGlobalsPt, PDRIVER_OBJECT pDriverObject, + PUNICODE_STRING pRegistryPathStr) +{ + RT_NOREF2(pDriverObject, pRegistryPathStr); + NDIS_PROTOCOL_CHARACTERISTICS PtChars; + NDIS_STRING NameStr; + + NdisInitUnicodeString(&NameStr, VBOXNETFLT_NAME_PROTOCOL); + + NdisZeroMemory(&PtChars, sizeof (PtChars)); + PTCHARS_40(PtChars).MajorNdisVersion = VBOXNETFLT_VERSION_PT_NDIS_MAJOR; + PTCHARS_40(PtChars).MinorNdisVersion = VBOXNETFLT_VERSION_PT_NDIS_MINOR; + + PTCHARS_40(PtChars).Name = NameStr; + PTCHARS_40(PtChars).OpenAdapterCompleteHandler = vboxNetFltWinPtOpenAdapterComplete; + PTCHARS_40(PtChars).CloseAdapterCompleteHandler = vboxNetFltWinPtCloseAdapterComplete; + PTCHARS_40(PtChars).SendCompleteHandler = vboxNetFltWinPtSendComplete; + PTCHARS_40(PtChars).TransferDataCompleteHandler = vboxNetFltWinPtTransferDataComplete; + PTCHARS_40(PtChars).ResetCompleteHandler = vboxNetFltWinPtResetComplete; + PTCHARS_40(PtChars).RequestCompleteHandler = vboxNetFltWinPtRequestComplete; + PTCHARS_40(PtChars).ReceiveHandler = vboxNetFltWinPtReceive; + PTCHARS_40(PtChars).ReceiveCompleteHandler = vboxNetFltWinPtReceiveComplete; + PTCHARS_40(PtChars).StatusHandler = vboxNetFltWinPtStatus; + PTCHARS_40(PtChars).StatusCompleteHandler = vboxNetFltWinPtStatusComplete; + PTCHARS_40(PtChars).BindAdapterHandler = vboxNetFltWinPtBindAdapter; + PTCHARS_40(PtChars).UnbindAdapterHandler = vboxNetFltWinPtUnbindAdapter; + PTCHARS_40(PtChars).UnloadHandler = vboxNetFltWinPtUnloadProtocol; +#if !defined(DEBUG_NETFLT_RECV) + PTCHARS_40(PtChars).ReceivePacketHandler = vboxNetFltWinPtReceivePacket; +#endif + PTCHARS_40(PtChars).PnPEventHandler = vboxNetFltWinPtPnPEvent; + + NDIS_STATUS Status; + NdisRegisterProtocol(&Status, &pGlobalsPt->hProtocol, &PtChars, sizeof (PtChars)); + Assert(Status == STATUS_SUCCESS); + return Status; +} + +/** + * deregister the protocol edge + */ +DECLHIDDEN(NDIS_STATUS) vboxNetFltWinPtDeregister(PVBOXNETFLTGLOBALS_PT pGlobalsPt) +{ + if (!pGlobalsPt->hProtocol) + return NDIS_STATUS_SUCCESS; + + NDIS_STATUS Status; + + NdisDeregisterProtocol(&Status, pGlobalsPt->hProtocol); + Assert (Status == NDIS_STATUS_SUCCESS); + if (Status == NDIS_STATUS_SUCCESS) + { + NdisZeroMemory(pGlobalsPt, sizeof (*pGlobalsPt)); + } + return Status; +} diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltP-win.h b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltP-win.h new file mode 100644 index 00000000..6658e33e --- /dev/null +++ b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltP-win.h @@ -0,0 +1,42 @@ +/* $Id: VBoxNetFltP-win.h $ */ +/** @file + * VBoxNetFltP-win.h - Bridged Networking Driver, Windows Specific Code. + * Protocol edge API + */ +/* + * Copyright (C) 2011-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * 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. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE 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. + */ + +#ifndef VBOX_INCLUDED_SRC_VBoxNetFlt_win_drv_VBoxNetFltP_win_h +#define VBOX_INCLUDED_SRC_VBoxNetFlt_win_drv_VBoxNetFltP_win_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#ifdef VBOXNETADP +# error "No protocol edge" +#endif +DECLHIDDEN(NDIS_STATUS) vboxNetFltWinPtRegister(PVBOXNETFLTGLOBALS_PT pGlobalsPt, PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegistryPathStr); +DECLHIDDEN(NDIS_STATUS) vboxNetFltWinPtDeregister(PVBOXNETFLTGLOBALS_PT pGlobalsPt); +DECLHIDDEN(NDIS_STATUS) vboxNetFltWinPtDoUnbinding(PVBOXNETFLTINS pNetFlt, bool bOnUnbind); +DECLHIDDEN(VOID) vboxNetFltWinPtRequestComplete(NDIS_HANDLE hContext, PNDIS_REQUEST pNdisRequest, NDIS_STATUS Status); +DECLHIDDEN(bool) vboxNetFltWinPtCloseInterface(PVBOXNETFLTINS pNetFlt, PNDIS_STATUS pStatus); +DECLHIDDEN(NDIS_STATUS) vboxNetFltWinPtDoBinding(PVBOXNETFLTINS pThis, PNDIS_STRING pOurDeviceName, PNDIS_STRING pBindToDeviceName); +#endif /* !VBOX_INCLUDED_SRC_VBoxNetFlt_win_drv_VBoxNetFltP_win_h */ diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltRt-win.cpp b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltRt-win.cpp new file mode 100644 index 00000000..957fc81e --- /dev/null +++ b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltRt-win.cpp @@ -0,0 +1,3640 @@ +/* $Id: VBoxNetFltRt-win.cpp $ */ +/** @file + * VBoxNetFltRt-win.cpp - Bridged Networking Driver, Windows Specific Runtime Code. + */ + +/* + * Copyright (C) 2011-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * 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. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE 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. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include "VBoxNetFltCmn-win.h" +#include <VBox/intnetinline.h> +#include <iprt/thread.h> + +#include <iprt/nt/tdikrnl.h> +#include <mstcpip.h> + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +/** represents the job element of the job queue + * see comments for VBOXNETFLT_JOB_QUEUE */ +typedef struct VBOXNETFLT_JOB +{ + /** link in the job queue */ + LIST_ENTRY ListEntry; + /** job function to be executed */ + PFNVBOXNETFLT_JOB_ROUTINE pfnRoutine; + /** parameter to be passed to the job function */ + PVOID pContext; + /** event that will be fired on job completion */ + KEVENT CompletionEvent; + /** true if the job manager should use the completion even for completion indication, false-otherwise*/ + bool bUseCompletionEvent; +} VBOXNETFLT_JOB, *PVBOXNETFLT_JOB; + +/** + * represents the queue of jobs processed by the worker thread + * + * we use the thread to process tasks which are required to be done at passive level + * our callbacks may be called at APC level by IntNet, there are some tasks that we can not create at APC, + * e.g. thread creation. This is why we schedule such jobs to the worker thread working at passive level + */ +typedef struct VBOXNETFLT_JOB_QUEUE +{ + /* jobs */ + LIST_ENTRY Jobs; + /* we are using ExInterlocked..List functions to access the jobs list */ + KSPIN_LOCK Lock; + /** this event is used to initiate a job worker thread kill */ + KEVENT KillEvent; + /** this event is used to notify a worker thread that jobs are added to the queue */ + KEVENT NotifyEvent; + /** worker thread */ + PKTHREAD pThread; +} VBOXNETFLT_JOB_QUEUE, *PVBOXNETFLT_JOB_QUEUE; + +typedef struct _CREATE_INSTANCE_CONTEXT +{ +#ifndef VBOXNETADP + PNDIS_STRING pOurName; + PNDIS_STRING pBindToName; +#else + NDIS_HANDLE hMiniportAdapter; + NDIS_HANDLE hWrapperConfigurationContext; +#endif + NDIS_STATUS Status; +} CREATE_INSTANCE_CONTEXT, *PCREATE_INSTANCE_CONTEXT; + +/*contexts used for our jobs */ +/* Attach context */ +typedef struct _ATTACH_INFO +{ + PVBOXNETFLTINS pNetFltIf; + PCREATE_INSTANCE_CONTEXT pCreateContext; + bool fRediscovery; + int Status; +} ATTACH_INFO, *PATTACH_INFO; + +/* general worker context */ +typedef struct _WORKER_INFO +{ + PVBOXNETFLTINS pNetFltIf; + int Status; +} WORKER_INFO, *PWORKER_INFO; + +/* idc initialization */ +typedef struct _INIT_IDC_INFO +{ + VBOXNETFLT_JOB Job; + bool bInitialized; + volatile bool bStop; + volatile int rc; + KEVENT hCompletionEvent; +} INIT_IDC_INFO, *PINIT_IDC_INFO; + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +/** global job queue. some operations are required to be done at passive level, e.g. thread creation, adapter bind/unbind initiation, + * while IntNet typically calls us APC_LEVEL, so we just create a system thread in our DriverEntry and enqueue the jobs to that thread */ +static VBOXNETFLT_JOB_QUEUE g_VBoxJobQueue; +volatile static bool g_bVBoxIdcInitialized; +INIT_IDC_INFO g_VBoxInitIdcInfo; + +/** + * The (common) global data. + */ +static VBOXNETFLTGLOBALS g_VBoxNetFltGlobals; +/* win-specific global data */ +VBOXNETFLTGLOBALS_WIN g_VBoxNetFltGlobalsWin = {0}; + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +#define LIST_ENTRY_2_JOB(pListEntry) \ + ( (PVBOXNETFLT_JOB)((uint8_t *)(pListEntry) - RT_UOFFSETOF(VBOXNETFLT_JOB, ListEntry)) ) + + +/********************************************************************************************************************************* +* Internal Functions * +*********************************************************************************************************************************/ +static int vboxNetFltWinAttachToInterface(PVBOXNETFLTINS pThis, void * pContext, bool fRediscovery); +static int vboxNetFltWinConnectIt(PVBOXNETFLTINS pThis); +static int vboxNetFltWinFiniIdc(); +static void vboxNetFltWinFiniNetFltBase(); +static int vboxNetFltWinInitNetFltBase(); +static int vboxNetFltWinFiniNetFlt(); +static int vboxNetFltWinStartInitIdcProbing(); +static int vboxNetFltWinStopInitIdcProbing(); + + + +/** makes the current thread to sleep for the given number of miliseconds */ +DECLHIDDEN(void) vboxNetFltWinSleep(ULONG milis) +{ + RTThreadSleep(milis); +} + +/** wait for the given device to be dereferenced */ +DECLHIDDEN(void) vboxNetFltWinWaitDereference(PVBOXNETFLT_WINIF_DEVICE pState) +{ +#ifdef DEBUG + uint64_t StartNanoTS = RTTimeSystemNanoTS(); + uint64_t CurNanoTS; +#endif + Assert(KeGetCurrentIrql() < DISPATCH_LEVEL); + + while (ASMAtomicUoReadU32((volatile uint32_t *)&pState->cReferences)) + { + vboxNetFltWinSleep(2); +#ifdef DEBUG + CurNanoTS = RTTimeSystemNanoTS(); + if (CurNanoTS - StartNanoTS > 20000000) + { + LogRel(("device not idle")); + AssertFailed(); +// break; + } +#endif + } +} + +/** + * mem functions + */ +/* allocates and zeroes the nonpaged memory of a given size */ +DECLHIDDEN(NDIS_STATUS) vboxNetFltWinMemAlloc(PVOID *ppvMemBuf, UINT cbLength) +{ +#ifdef DEBUG_NETFLT_USE_EXALLOC + *ppvMemBuf = ExAllocatePoolWithTag(NonPagedPool, cbLength, VBOXNETFLT_MEM_TAG); + if (*ppvMemBuf) + { + NdisZeroMemory(*ppvMemBuf, cbLength); + return NDIS_STATUS_SUCCESS; + } + return NDIS_STATUS_FAILURE; +#else + NDIS_STATUS fStatus = NdisAllocateMemoryWithTag(ppvMemBuf, cbLength, VBOXNETFLT_MEM_TAG); + if (fStatus == NDIS_STATUS_SUCCESS) + NdisZeroMemory(*ppvMemBuf, cbLength); + return fStatus; +#endif +} + +/* frees memory allocated with vboxNetFltWinMemAlloc */ +DECLHIDDEN(void) vboxNetFltWinMemFree(PVOID pvMemBuf) +{ +#ifdef DEBUG_NETFLT_USE_EXALLOC + ExFreePool(pvMemBuf); +#else + NdisFreeMemory(pvMemBuf, 0, 0); +#endif +} + +#ifndef VBOXNETFLT_NO_PACKET_QUEUE + +/* initializes packet info pool and allocates the cSize packet infos for the pool */ +static NDIS_STATUS vboxNetFltWinPpAllocatePacketInfoPool(PVBOXNETFLT_PACKET_INFO_POOL pPool, UINT cSize) +{ + UINT cbBufSize = sizeof(PACKET_INFO)*cSize; + PACKET_INFO * pPacketInfos; + NDIS_STATUS fStatus; + UINT i; + + Assert(cSize > 0); + + INIT_INTERLOCKED_PACKET_QUEUE(&pPool->Queue); + + fStatus = vboxNetFltWinMemAlloc((PVOID*)&pPacketInfos, cbBufSize); + + if (fStatus == NDIS_STATUS_SUCCESS) + { + PVBOXNETFLTPACKET_INFO pInfo; + pPool->pBuffer = pPacketInfos; + + for (i = 0; i < cSize; i++) + { + pInfo = &pPacketInfos[i]; + vboxNetFltWinQuEnqueueTail(&pPool->Queue.Queue, pInfo); + pInfo->pPool = pPool; + } + } + else + { + AssertFailed(); + } + + return fStatus; +} + +/* frees the packet info pool */ +VOID vboxNetFltWinPpFreePacketInfoPool(PVBOXNETFLT_PACKET_INFO_POOL pPool) +{ + vboxNetFltWinMemFree(pPool->pBuffer); + + FINI_INTERLOCKED_PACKET_QUEUE(&pPool->Queue) +} + +#endif + +/** + * copies one string to another. in case the destination string size is not enough to hold the complete source string + * does nothing and returns NDIS_STATUS_RESOURCES . + */ +DECLHIDDEN(NDIS_STATUS) vboxNetFltWinCopyString(PNDIS_STRING pDst, PNDIS_STRING pSrc) +{ + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + + if (pDst != pSrc) + { + if (pDst->MaximumLength < pSrc->Length) + { + AssertFailed(); + Status = NDIS_STATUS_RESOURCES; + } + else + { + pDst->Length = pSrc->Length; + + if (pDst->Buffer != pSrc->Buffer) + { + NdisMoveMemory(pDst->Buffer, pSrc->Buffer, pSrc->Length); + } + } + } + return Status; +} + +/************************************************************************************ + * PINTNETSG pSG manipulation functions + ************************************************************************************/ + +/* moves the contents of the given NDIS_BUFFER and all other buffers chained to it to the PINTNETSG + * the PINTNETSG is expected to contain one segment whose bugger is large enough to maintain + * the contents of the given NDIS_BUFFER and all other buffers chained to it */ +static NDIS_STATUS vboxNetFltWinNdisBufferMoveToSG0(PNDIS_BUFFER pBuffer, PINTNETSG pSG) +{ + PINTNETSEG paSeg; + uint8_t * ptr; + PVOID pVirtualAddress; + UINT cbCurrentLength; + NDIS_STATUS fStatus = NDIS_STATUS_SUCCESS; + + Assert(pSG->cSegsAlloc == 1); + + paSeg = pSG->aSegs; + ptr = (uint8_t*)paSeg->pv; + paSeg->cb = 0; + paSeg->Phys = NIL_RTHCPHYS; + pSG->cbTotal = 0; + + Assert(paSeg->pv); + + while (pBuffer) + { + NdisQueryBufferSafe(pBuffer, &pVirtualAddress, &cbCurrentLength, NormalPagePriority); + + if (!pVirtualAddress) + { + fStatus = NDIS_STATUS_FAILURE; + break; + } + + pSG->cbTotal += cbCurrentLength; + paSeg->cb += cbCurrentLength; + NdisMoveMemory(ptr, pVirtualAddress, cbCurrentLength); + ptr += cbCurrentLength; + + NdisGetNextBuffer(pBuffer, &pBuffer); + } + + if (fStatus == NDIS_STATUS_SUCCESS) + { + pSG->cSegsUsed = 1; + Assert(pSG->cbTotal == paSeg->cb); + } + return fStatus; +} + +/* converts the PNDIS_BUFFER to PINTNETSG by making the PINTNETSG segments to point to the memory buffers the + * ndis buffer(s) point to (as opposed to vboxNetFltWinNdisBufferMoveToSG0 which copies the memory from ndis buffers(s) to PINTNETSG) */ +static NDIS_STATUS vboxNetFltWinNdisBuffersToSG(PNDIS_BUFFER pBuffer, PINTNETSG pSG) +{ + UINT cSegs = 0; + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + PVOID pVirtualAddress; + UINT cbCurrentLength; + + while (pBuffer) + { + NdisQueryBufferSafe(pBuffer, &pVirtualAddress, &cbCurrentLength, NormalPagePriority); + + if (!pVirtualAddress) + { + Status = NDIS_STATUS_FAILURE; + break; + } + + pSG->cbTotal += cbCurrentLength; + pSG->aSegs[cSegs].cb = cbCurrentLength; + pSG->aSegs[cSegs].pv = pVirtualAddress; + pSG->aSegs[cSegs].Phys = NIL_RTHCPHYS; + cSegs++; + + NdisGetNextBuffer(pBuffer, &pBuffer); + } + + AssertFatal(cSegs <= pSG->cSegsAlloc); + + if (Status == NDIS_STATUS_SUCCESS) + { + pSG->cSegsUsed = cSegs; + } + + return Status; +} + +static void vboxNetFltWinDeleteSG(PINTNETSG pSG) +{ + vboxNetFltWinMemFree(pSG); +} + +static PINTNETSG vboxNetFltWinCreateSG(uint32_t cSegs) +{ + PINTNETSG pSG; + NTSTATUS Status = vboxNetFltWinMemAlloc((PVOID*)&pSG, RT_UOFFSETOF_DYN(INTNETSG, aSegs[cSegs])); + if (Status == STATUS_SUCCESS) + { + IntNetSgInitTempSegs(pSG, 0 /*cbTotal*/, cSegs, 0 /*cSegsUsed*/); + return pSG; + } + + return NULL; +} + +/************************************************************************************ + * packet queue functions + ************************************************************************************/ +#ifndef VBOXNETFLT_NO_PACKET_QUEUE +#if !defined(VBOXNETADP) +static NDIS_STATUS vboxNetFltWinQuPostPacket(PVBOXNETFLTINS pNetFlt, PNDIS_PACKET pPacket, PINTNETSG pSG, uint32_t fFlags +# ifdef DEBUG_NETFLT_PACKETS + , PNDIS_PACKET pTmpPacket +# endif + ) +{ + NDIS_STATUS Status; + PNDIS_PACKET pMyPacket; + bool bSrcHost = fFlags & PACKET_SRC_HOST; + + LogFlow(("posting packet back to driver stack..\n")); + + if (!pPacket) + { + /* INTNETSG was in the packet queue, create a new NdisPacket from INTNETSG*/ + pMyPacket = vboxNetFltWinNdisPacketFromSG(pNetFlt, + pSG, /* PINTNETSG */ + pSG, /* PVOID pBufToFree */ + bSrcHost, /* bool bToWire */ + false); /* bool bCopyMemory */ + + Assert(pMyPacket); + + NDIS_SET_PACKET_STATUS(pMyPacket, NDIS_STATUS_SUCCESS); + + DBG_CHECK_PACKET_AND_SG(pMyPacket, pSG); + +#ifdef DEBUG_NETFLT_PACKETS + Assert(pTmpPacket); + + DBG_CHECK_PACKET_AND_SG(pTmpPacket, pSG); + + DBG_CHECK_PACKETS(pTmpPacket, pMyPacket); +#endif + + LogFlow(("non-ndis packet info, packet created (%p)\n", pMyPacket)); + } + else + { + /* NDIS_PACKET was in the packet queue */ + DBG_CHECK_PACKET_AND_SG(pPacket, pSG); + + if (!(fFlags & PACKET_MINE)) + { + /* the packet is the one that was passed to us in send/receive callback + * According to the DDK, we can not post it further, + * instead we should allocate our own packet. + * So, allocate our own packet (pMyPacket) and copy the packet info there */ + if (bSrcHost) + { + Status = vboxNetFltWinPrepareSendPacket(pNetFlt, pPacket, &pMyPacket/*, true*/); + LogFlow(("packet from wire, packet created (%p)\n", pMyPacket)); + } + else + { + Status = vboxNetFltWinPrepareRecvPacket(pNetFlt, pPacket, &pMyPacket, false); + LogFlow(("packet from wire, packet created (%p)\n", pMyPacket)); + } + } + else + { + /* the packet enqueued is ours, simply assign pMyPacket and zero pPacket */ + pMyPacket = pPacket; + pPacket = NULL; + } + Assert(pMyPacket); + } + + if (pMyPacket) + { + /* we have successfully initialized our packet, post it to the host or to the wire */ + if (bSrcHost) + { +#if defined(DEBUG_NETFLT_PACKETS) || !defined(VBOX_LOOPBACK_USEFLAGS) + vboxNetFltWinLbPutSendPacket(pNetFlt, pMyPacket, false /* bFromIntNet */); +#endif + NdisSend(&Status, pNetFlt->u.s.hBinding, pMyPacket); + + if (Status != NDIS_STATUS_PENDING) + { +#if defined(DEBUG_NETFLT_PACKETS) || !defined(VBOX_LOOPBACK_USEFLAGS) + /* the status is NOT pending, complete the packet */ + bool bTmp = vboxNetFltWinLbRemoveSendPacket(pNetFlt, pMyPacket); + Assert(bTmp); +#endif + if (pPacket) + { + LogFlow(("status is not pending, completing packet (%p)\n", pPacket)); + + NdisIMCopySendCompletePerPacketInfo (pPacket, pMyPacket); + + NdisFreePacket(pMyPacket); + } + else + { + /* should never be here since the PINTNETSG is stored only when the underlying miniport + * indicates NDIS_STATUS_RESOURCES, we should never have this when processing + * the "from-host" packets */ + AssertFailed(); + LogFlow(("status is not pending, freeing myPacket (%p)\n", pMyPacket)); + vboxNetFltWinFreeSGNdisPacket(pMyPacket, false); + } + } + } + else + { + NdisMIndicateReceivePacket(pNetFlt->u.s.hMiniport, &pMyPacket, 1); + + Status = NDIS_STATUS_PENDING; + /* the packet receive completion is always indicated via MiniportReturnPacket */ + } + } + else + { + /*we failed to create our packet */ + AssertFailed(); + Status = NDIS_STATUS_FAILURE; + } + + return Status; +} +#endif + +static bool vboxNetFltWinQuProcessInfo(PVBOXNETFLTINS pNetFltIf, PPACKET_QUEUE_WORKER pWorker, PVOID pvPacket, const UINT fFlags) +#else +DECLHIDDEN(bool) vboxNetFltWinPostIntnet(PVBOXNETFLTINS pNetFltIf, PVOID pvPacket, const UINT fFlags) +#endif +{ + PNDIS_PACKET pPacket = NULL; + PINTNETSG pSG = NULL; + NDIS_STATUS Status; +#ifndef VBOXNETADP + bool bSrcHost; + bool bDropIt; +# ifndef VBOXNETFLT_NO_PACKET_QUEUE + bool bPending; +# endif +#endif +#ifdef VBOXNETFLT_NO_PACKET_QUEUE + bool bDeleteSG = false; +#endif +#ifdef DEBUG_NETFLT_PACKETS + /* packet used for matching */ + PNDIS_PACKET pTmpPacket = NULL; +#endif + +#ifndef VBOXNETADP + bSrcHost = (fFlags & VBOXNETFLT_PACKET_SRC_HOST) != 0; +#endif + + /* we first need to obtain the INTNETSG to be passed to intnet */ + + /* the queue may contain two "types" of packets: + * the NDIS_PACKET and the INTNETSG. + * I.e. on send/receive we typically enqueue the NDIS_PACKET passed to us by ndis, + * however in case our ProtocolReceive is called or the packet's status is set to NDIS_STSTUS_RESOURCES + * in ProtocolReceivePacket, we must return the packet immediately on ProtocolReceive*** exit + * In this case we allocate the INTNETSG, copy the ndis packet data there and enqueue it. + * In this case the packet info flags has the VBOXNETFLT_PACKET_SG fag set + * + * Besides that the NDIS_PACKET contained in the queue could be either the one passed to us in our send/receive callback + * or the one created by us. The latter is possible in case our ProtocolReceive callback is called and we call NdisTransferData + * in this case we need to allocate the packet the data to be transferred to. + * If the enqueued packet is the one allocated by us the VBOXNETFLT_PACKET_MINE flag is set + * */ + if ((fFlags & VBOXNETFLT_PACKET_SG) == 0) + { + /* we have NDIS_PACKET enqueued, we need to convert it to INTNETSG to be passed to intnet */ + PNDIS_BUFFER pCurrentBuffer = NULL; + UINT cBufferCount; + UINT cbPacketLength; + + pPacket = (PNDIS_PACKET)pvPacket; + + LogFlow(("ndis packet info, packet (%p)\n", pPacket)); + + LogFlow(("preparing pSG")); + NdisQueryPacket(pPacket, NULL, &cBufferCount, &pCurrentBuffer, &cbPacketLength); + Assert(cBufferCount); + +#ifdef VBOXNETFLT_NO_PACKET_QUEUE + pSG = vboxNetFltWinCreateSG(cBufferCount); +#else + /* we can not allocate the INTNETSG on stack since in this case we may get stack overflow + * somewhere outside of our driver (3 pages of system thread stack does not seem to be enough) + * + * since we have a "serialized" packet processing, i.e. all packets are being processed and passed + * to intnet by this thread, we just use one previously allocated INTNETSG which is stored in PVBOXNETFLTINS */ + pSG = pWorker->pSG; + + if (cBufferCount > pSG->cSegsAlloc) + { + pSG = vboxNetFltWinCreateSG(cBufferCount + 2); + if (pSG) + { + vboxNetFltWinDeleteSG(pWorker->pSG); + pWorker->pSG = pSG; + } + else + { + LogRel(("Failed to reallocate the pSG\n")); + } + } +#endif + + if (pSG) + { +#ifdef VBOXNETFLT_NO_PACKET_QUEUE + bDeleteSG = true; +#endif + /* reinitialize */ + IntNetSgInitTempSegs(pSG, 0 /*cbTotal*/, pSG->cSegsAlloc, 0 /*cSegsUsed*/); + + /* convert the ndis buffers to INTNETSG */ + Status = vboxNetFltWinNdisBuffersToSG(pCurrentBuffer, pSG); + if (Status != NDIS_STATUS_SUCCESS) + { + pSG = NULL; + } + else + { + DBG_CHECK_PACKET_AND_SG(pPacket, pSG); + } + } + } + else + { + /* we have the INTNETSG enqueued. (see the above comment explaining why/when this may happen) + * just use the INTNETSG to pass it to intnet */ +#ifndef VBOXNETADP + /* the PINTNETSG is stored only when the underlying miniport + * indicates NDIS_STATUS_RESOURCES, we should never have this when processing + * the "from-host" packedts */ + Assert(!bSrcHost); +#endif + pSG = (PINTNETSG)pvPacket; + + LogFlow(("not ndis packet info, pSG (%p)\n", pSG)); + } + +#ifdef DEBUG_NETFLT_PACKETS + if (!pPacket && !pTmpPacket) + { + /* create tmp packet that woud be used for matching */ + pTmpPacket = vboxNetFltWinNdisPacketFromSG(pNetFltIf, + pSG, /* PINTNETSG */ + pSG, /* PVOID pBufToFree */ + bSrcHost, /* bool bToWire */ + true); /* bool bCopyMemory */ + + NDIS_SET_PACKET_STATUS(pTmpPacket, NDIS_STATUS_SUCCESS); + + DBG_CHECK_PACKET_AND_SG(pTmpPacket, pSG); + + Assert(pTmpPacket); + } +#endif + do + { +#ifndef VBOXNETADP + /* the pSG was successfully initialized, post it to the netFlt*/ + bDropIt = pSG ? pNetFltIf->pSwitchPort->pfnRecv(pNetFltIf->pSwitchPort, NULL /* pvIf */, pSG, + bSrcHost ? INTNETTRUNKDIR_HOST : INTNETTRUNKDIR_WIRE + ) + : false; +#else + if (pSG) + { + pNetFltIf->pSwitchPort->pfnRecv(pNetFltIf->pSwitchPort, NULL /* pvIf */, pSG, INTNETTRUNKDIR_HOST); + STATISTIC_INCREASE(pNetFltIf->u.s.WinIf.cTxSuccess); + } + else + { + STATISTIC_INCREASE(pNetFltIf->u.s.WinIf.cTxError); + } +#endif + +#ifndef VBOXNETFLT_NO_PACKET_QUEUE + +# if !defined(VBOXNETADP) + if (!bDropIt) + { + Status = vboxNetFltWinQuPostPacket(pNetFltIf, pPacket, pSG, fFlags +# ifdef DEBUG_NETFLT_PACKETS + , pTmpPacket +# endif + ); + + if (Status == NDIS_STATUS_PENDING) + { + /* we will process packet completion in the completion routine */ + bPending = true; + break; + } + } + else +# endif + { + Status = NDIS_STATUS_SUCCESS; + } + + /* drop it */ + if (pPacket) + { + if (!(fFlags & PACKET_MINE)) + { +# if !defined(VBOXNETADP) + /* complete the packets */ + if (fFlags & PACKET_SRC_HOST) + { +# endif +/* NDIS_SET_PACKET_STATUS(pPacket, Status); */ + NdisMSendComplete(pNetFltIf->u.s.hMiniport, pPacket, Status); +# if !defined(VBOXNETADP) + } + else + { +# endif +# ifndef VBOXNETADP + NdisReturnPackets(&pPacket, 1); +# endif +# if !defined(VBOXNETADP) + } +# endif + } + else + { + Assert(!(fFlags & PACKET_SRC_HOST)); + vboxNetFltWinFreeSGNdisPacket(pPacket, true); + } + } + else + { + Assert(pSG); + vboxNetFltWinMemFree(pSG); + } +# ifndef VBOXNETADP + bPending = false; +# endif + } while (0); + +#ifdef DEBUG_NETFLT_PACKETS + if (pTmpPacket) + { + vboxNetFltWinFreeSGNdisPacket(pTmpPacket, true); + } +#endif + +#ifndef VBOXNETADP + return bPending; +#else + return false; +#endif +#else /* #ifdef VBOXNETFLT_NO_PACKET_QUEUE */ + } while (0); + + if (bDeleteSG) + vboxNetFltWinMemFree(pSG); + +# ifndef VBOXNETADP + return bDropIt; +# else + return true; +# endif +#endif +} +#ifndef VBOXNETFLT_NO_PACKET_QUEUE +/* + * thread start function for the thread which processes the packets enqueued in our send and receive callbacks called by ndis + * + * ndis calls us at DISPATCH_LEVEL, while IntNet is using kernel functions which require Irql<DISPATCH_LEVEL + * this is why we can not immediately post packets to IntNet from our sen/receive callbacks + * instead we put the incoming packets to the queue and maintain the system thread running at passive level + * which processes the queue and posts the packets to IntNet, and further to the host or to the wire. + */ +static VOID vboxNetFltWinQuPacketQueueWorkerThreadProc(PVBOXNETFLTINS pNetFltIf) +{ + bool fResume = true; + NTSTATUS fStatus; + PPACKET_QUEUE_WORKER pWorker = &pNetFltIf->u.s.PacketQueueWorker; + + PVOID apEvents[] = { + (PVOID)&pWorker->KillEvent, + (PVOID)&pWorker->NotifyEvent + }; + + while (fResume) + { + uint32_t cNumProcessed; + uint32_t cNumPostedToHostWire; + + fStatus = KeWaitForMultipleObjects(RT_ELEMENTS(apEvents), apEvents, WaitAny, Executive, KernelMode, FALSE, NULL, NULL); + if (!NT_SUCCESS(fStatus) || fStatus == STATUS_WAIT_0) + { + /* "kill" event was set + * will process queued packets and exit */ + fResume = false; + } + + LogFlow(("processing vboxNetFltWinQuPacketQueueWorkerThreadProc\n")); + + cNumProcessed = 0; + cNumPostedToHostWire = 0; + + do + { + PVBOXNETFLTPACKET_INFO pInfo; + +#ifdef DEBUG_NETFLT_PACKETS + /* packet used for matching */ + PNDIS_PACKET pTmpPacket = NULL; +#endif + + /** @todo FIXME: !!! the better approach for performance would be to dequeue all packets at once + * and then go through all dequeued packets + * the same should be done for enqueue !!! */ + pInfo = vboxNetFltWinQuInterlockedDequeueHead(&pWorker->PacketQueue); + + if (!pInfo) + { + break; + } + + LogFlow(("found info (0x%p)\n", pInfo)); + + if (vboxNetFltWinQuProcessInfo(pNetFltIf, pWorker, pInfo->pPacket, pInfo->fFlags)) + { + cNumPostedToHostWire++; + } + + vboxNetFltWinPpFreePacketInfo(pInfo); + + cNumProcessed++; + } while (TRUE); + + if (cNumProcessed) + { + vboxNetFltWinDecReferenceNetFlt(pNetFltIf, cNumProcessed); + + Assert(cNumProcessed >= cNumPostedToHostWire); + + if (cNumProcessed != cNumPostedToHostWire) + { + vboxNetFltWinDecReferenceWinIf(pNetFltIf, cNumProcessed - cNumPostedToHostWire); + } + } + } + + PsTerminateSystemThread(STATUS_SUCCESS); +} +#endif +/** + * thread start function for the job processing thread + * + * see comments for PVBOXNETFLT_JOB_QUEUE + */ +static VOID vboxNetFltWinJobWorkerThreadProc(PVBOXNETFLT_JOB_QUEUE pQueue) +{ + bool fResume = true; + NTSTATUS Status; + + PVOID apEvents[] = { + (PVOID)&pQueue->KillEvent, + (PVOID)&pQueue->NotifyEvent, + }; + + do + { + Status = KeWaitForMultipleObjects(RT_ELEMENTS(apEvents), apEvents, WaitAny, Executive, KernelMode, FALSE, NULL, NULL); + Assert(NT_SUCCESS(Status)); + if (!NT_SUCCESS(Status) || Status == STATUS_WAIT_0) + { + /* will process queued jobs and exit */ + Assert(Status == STATUS_WAIT_0); + fResume = false; + } + + do + { + PLIST_ENTRY pJobEntry = ExInterlockedRemoveHeadList(&pQueue->Jobs, &pQueue->Lock); + PVBOXNETFLT_JOB pJob; + + if (!pJobEntry) + break; + + pJob = LIST_ENTRY_2_JOB(pJobEntry); + + Assert(KeGetCurrentIrql() == PASSIVE_LEVEL); + pJob->pfnRoutine(pJob->pContext); + Assert(KeGetCurrentIrql() == PASSIVE_LEVEL); + + if (pJob->bUseCompletionEvent) + { + KeSetEvent(&pJob->CompletionEvent, 1, FALSE); + } + } while (TRUE); + } while (fResume); + + Assert(Status == STATUS_WAIT_0); + + PsTerminateSystemThread(STATUS_SUCCESS); +} + +/** + * enqueues the job to the job queue to be processed by the job worker thread + * see comments for PVBOXNETFLT_JOB_QUEUE + */ +static VOID vboxNetFltWinJobEnqueueJob(PVBOXNETFLT_JOB_QUEUE pQueue, PVBOXNETFLT_JOB pJob, bool bEnqueueHead) +{ + if (bEnqueueHead) + { + ExInterlockedInsertHeadList(&pQueue->Jobs, &pJob->ListEntry, &pQueue->Lock); + } + else + { + ExInterlockedInsertTailList(&pQueue->Jobs, &pJob->ListEntry, &pQueue->Lock); + } + + KeSetEvent(&pQueue->NotifyEvent, 1, FALSE); +} + +DECLINLINE(VOID) vboxNetFltWinJobInit(PVBOXNETFLT_JOB pJob, PFNVBOXNETFLT_JOB_ROUTINE pfnRoutine, PVOID pContext, bool bUseEvent) +{ + pJob->pfnRoutine = pfnRoutine; + pJob->pContext = pContext; + pJob->bUseCompletionEvent = bUseEvent; + if (bUseEvent) + KeInitializeEvent(&pJob->CompletionEvent, NotificationEvent, FALSE); +} + +/** + * enqueues the job to the job queue to be processed by the job worker thread and + * blocks until the job is done + * see comments for PVBOXNETFLT_JOB_QUEUE + */ +static VOID vboxNetFltWinJobSynchExec(PVBOXNETFLT_JOB_QUEUE pQueue, PFNVBOXNETFLT_JOB_ROUTINE pfnRoutine, PVOID pContext) +{ + VBOXNETFLT_JOB Job; + + Assert(KeGetCurrentIrql() < DISPATCH_LEVEL); + + vboxNetFltWinJobInit(&Job, pfnRoutine, pContext, true); + + vboxNetFltWinJobEnqueueJob(pQueue, &Job, false); + + KeWaitForSingleObject(&Job.CompletionEvent, Executive, KernelMode, FALSE, NULL); +} + +/** + * enqueues the job to be processed by the job worker thread at passive level and + * blocks until the job is done + */ +DECLHIDDEN(VOID) vboxNetFltWinJobSynchExecAtPassive(PFNVBOXNETFLT_JOB_ROUTINE pfnRoutine, PVOID pContext) +{ + vboxNetFltWinJobSynchExec(&g_VBoxJobQueue, pfnRoutine, pContext); +} + +/** + * helper function used for system thread creation + */ +static NTSTATUS vboxNetFltWinQuCreateSystemThread(PKTHREAD *ppThread, PKSTART_ROUTINE pfnStartRoutine, PVOID pvStartContext) +{ + OBJECT_ATTRIBUTES ObjectAttributes; + Assert(KeGetCurrentIrql() == PASSIVE_LEVEL); + + InitializeObjectAttributes(&ObjectAttributes, NULL, OBJ_KERNEL_HANDLE, NULL, NULL); + + HANDLE hThread; + NTSTATUS Status = PsCreateSystemThread(&hThread, THREAD_ALL_ACCESS, &ObjectAttributes, NULL, NULL, (PKSTART_ROUTINE)pfnStartRoutine, pvStartContext); + Assert(Status == STATUS_SUCCESS); + if (Status == STATUS_SUCCESS) + { + Status = ObReferenceObjectByHandle(hThread, THREAD_ALL_ACCESS, NULL, KernelMode, (PVOID*)ppThread, NULL); + Assert(Status == STATUS_SUCCESS); + ZwClose(hThread); + if (Status == STATUS_SUCCESS) + { + return STATUS_SUCCESS; + } + + /** @todo how would we fail in this case ?*/ + } + return Status; +} + +/** + * initialize the job queue + * see comments for PVBOXNETFLT_JOB_QUEUE + */ +static NTSTATUS vboxNetFltWinJobInitQueue(PVBOXNETFLT_JOB_QUEUE pQueue) +{ + NTSTATUS fStatus; + + Assert(KeGetCurrentIrql() == PASSIVE_LEVEL); + + NdisZeroMemory(pQueue, sizeof(VBOXNETFLT_JOB_QUEUE)); + + KeInitializeEvent(&pQueue->KillEvent, NotificationEvent, FALSE); + + KeInitializeEvent(&pQueue->NotifyEvent, SynchronizationEvent, FALSE); + + InitializeListHead(&pQueue->Jobs); + + fStatus = vboxNetFltWinQuCreateSystemThread(&pQueue->pThread, (PKSTART_ROUTINE)vboxNetFltWinJobWorkerThreadProc, pQueue); + if (fStatus != STATUS_SUCCESS) + { + pQueue->pThread = NULL; + } + else + { + Assert(pQueue->pThread); + } + + return fStatus; +} + +/** + * deinitialize the job queue + * see comments for PVBOXNETFLT_JOB_QUEUE + */ +static void vboxNetFltWinJobFiniQueue(PVBOXNETFLT_JOB_QUEUE pQueue) +{ + Assert(KeGetCurrentIrql() == PASSIVE_LEVEL); + + if (pQueue->pThread) + { + KeSetEvent(&pQueue->KillEvent, 0, FALSE); + + KeWaitForSingleObject(pQueue->pThread, Executive, + KernelMode, FALSE, NULL); + } +} + +#ifndef VBOXNETFLT_NO_PACKET_QUEUE + +/** + * initializes the packet queue + * */ +DECLHIDDEN(NTSTATUS) vboxNetFltWinQuInitPacketQueue(PVBOXNETFLTINS pInstance) +{ + NTSTATUS Status; + PPACKET_QUEUE_WORKER pWorker = &pInstance->u.s.PacketQueueWorker; + + AssertFatal(!pWorker->pSG); + + Assert(KeGetCurrentIrql() == PASSIVE_LEVEL); + + KeInitializeEvent(&pWorker->KillEvent, NotificationEvent, FALSE); + + KeInitializeEvent(&pWorker->NotifyEvent, SynchronizationEvent, FALSE); + + INIT_INTERLOCKED_PACKET_QUEUE(&pWorker->PacketQueue); + + do + { + Status = vboxNetFltWinPpAllocatePacketInfoPool(&pWorker->PacketInfoPool, VBOXNETFLT_PACKET_INFO_POOL_SIZE); + + if (Status == NDIS_STATUS_SUCCESS) + { + pWorker->pSG = vboxNetFltWinCreateSG(PACKET_QUEUE_SG_SEGS_ALLOC); + if (!pWorker->pSG) + { + Status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + Status = vboxNetFltWinQuCreateSystemThread(&pWorker->pThread, (PKSTART_ROUTINE)vboxNetFltWinQuPacketQueueWorkerThreadProc, pInstance); + if (Status != STATUS_SUCCESS) + { + vboxNetFltWinPpFreePacketInfoPool(&pWorker->PacketInfoPool); + vboxNetFltWinMemFree(pWorker->pSG); + pWorker->pSG = NULL; + break; + } + } + + } while (0); + + return Status; +} + +/* + * deletes the packet queue + */ +DECLHIDDEN(void) vboxNetFltWinQuFiniPacketQueue(PVBOXNETFLTINS pInstance) +{ + PINTNETSG pSG; + PPACKET_QUEUE_WORKER pWorker = &pInstance->u.s.PacketQueueWorker; + Assert(KeGetCurrentIrql() < DISPATCH_LEVEL); + + /* using the pPacketQueueSG as an indicator that the packet queue is initialized */ + RTSpinlockAcquire((pInstance)->hSpinlock); + if (pWorker->pSG) + { + pSG = pWorker->pSG; + pWorker->pSG = NULL; + RTSpinlockRelease((pInstance)->hSpinlock); + KeSetEvent(&pWorker->KillEvent, 0, FALSE); + + KeWaitForSingleObject(pWorker->pThread, Executive, + KernelMode, FALSE, NULL); + + vboxNetFltWinPpFreePacketInfoPool(&pWorker->PacketInfoPool); + + vboxNetFltWinDeleteSG(pSG); + + FINI_INTERLOCKED_PACKET_QUEUE(&pWorker->PacketQueue); + } + else + { + RTSpinlockRelease((pInstance)->hSpinlock); + } +} + +#endif + +/* + * creates the INTNETSG containing one segment pointing to the buffer of size cbBufSize + * the INTNETSG created should be cleaned with vboxNetFltWinMemFree + */ +DECLHIDDEN(NDIS_STATUS) vboxNetFltWinAllocSG(UINT cbPacket, PINTNETSG *ppSG) +{ + NDIS_STATUS Status; + PINTNETSG pSG; + + /* allocation: + * 1. SG_PACKET - with one aSegs pointing to + * 2. buffer of cbPacket containing the entire packet */ + AssertCompileSizeAlignment(INTNETSG, sizeof(PVOID)); + Status = vboxNetFltWinMemAlloc((PVOID*)&pSG, cbPacket + sizeof(INTNETSG)); + if (Status == NDIS_STATUS_SUCCESS) + { + IntNetSgInitTemp(pSG, pSG + 1, cbPacket); + LogFlow(("pSG created (%p)\n", pSG)); + *ppSG = pSG; + } + return Status; +} + +#ifndef VBOXNETFLT_NO_PACKET_QUEUE +/** + * put the packet info to the queue + */ +DECLINLINE(void) vboxNetFltWinQuEnqueueInfo(PVBOXNETFLTPACKET_QUEUE_WORKER pWorker, PVBOXNETFLTPACKET_INFO pInfo) +{ + vboxNetFltWinQuInterlockedEnqueueTail(&pWorker->PacketQueue, pInfo); + + KeSetEvent(&pWorker->NotifyEvent, IO_NETWORK_INCREMENT, FALSE); +} + +/** + * puts the packet to the queue + * + * @return NDIST_STATUS_SUCCESS iff the packet was enqueued successfully + * and error status otherwise. + * NOTE: that the success status does NOT mean that the packet processing is completed, but only that it was enqueued successfully + * the packet can be returned to the caller protocol/moniport only in case the bReleasePacket was set to true (in this case the copy of the packet was enqueued) + * or if vboxNetFltWinQuEnqueuePacket failed, i.e. the packet was NOT enqueued + */ +DECLHIDDEN(NDIS_STATUS) vboxNetFltWinQuEnqueuePacket(PVBOXNETFLTINS pInstance, PVOID pPacket, const UINT fPacketFlags) +{ + PVBOXNETFLT_PACKET_INFO pInfo; + PVBOXNETFLT_PACKET_QUEUE_WORKER pWorker = &pInstance->u.s.PacketQueueWorker; + NDIS_STATUS fStatus = NDIS_STATUS_SUCCESS; + + do + { + if (fPacketFlags & PACKET_COPY) + { + PNDIS_BUFFER pBuffer = NULL; + UINT cBufferCount; + UINT uBytesCopied = 0; + UINT cbPacketLength; + PINTNETSG pSG; + + /* the packet is Ndis packet */ + Assert(!(fPacketFlags & PACKET_SG)); + Assert(!(fPacketFlags & PACKET_MINE)); + + NdisQueryPacket((PNDIS_PACKET)pPacket, + NULL, + &cBufferCount, + &pBuffer, + &cbPacketLength); + + + Assert(cBufferCount); + + fStatus = vboxNetFltWinAllocSG(cbPacketLength, &pSG); + if (fStatus != NDIS_STATUS_SUCCESS) + { + AssertFailed(); + break; + } + + pInfo = vboxNetFltWinPpAllocPacketInfo(&pWorker->PacketInfoPool); + + if (!pInfo) + { + AssertFailed(); + /** @todo what status to set? */ + fStatus = NDIS_STATUS_FAILURE; + vboxNetFltWinMemFree(pSG); + break; + } + + Assert(pInfo->pPool); + + /* the packet we are queueing is SG, add PACKET_SG to flags */ + SET_FLAGS_TO_INFO(pInfo, fPacketFlags | PACKET_SG); + SET_PACKET_TO_INFO(pInfo, pSG); + + fStatus = vboxNetFltWinNdisBufferMoveToSG0(pBuffer, pSG); + if (fStatus != NDIS_STATUS_SUCCESS) + { + AssertFailed(); + vboxNetFltWinPpFreePacketInfo(pInfo); + vboxNetFltWinMemFree(pSG); + break; + } + + DBG_CHECK_PACKET_AND_SG((PNDIS_PACKET)pPacket, pSG); + } + else + { + pInfo = vboxNetFltWinPpAllocPacketInfo(&pWorker->PacketInfoPool); + + if (!pInfo) + { + AssertFailed(); + /** @todo what status to set? */ + fStatus = NDIS_STATUS_FAILURE; + break; + } + + Assert(pInfo->pPool); + + SET_FLAGS_TO_INFO(pInfo, fPacketFlags); + SET_PACKET_TO_INFO(pInfo, pPacket); + } + + vboxNetFltWinQuEnqueueInfo(pWorker, pInfo); + + } while (0); + + return fStatus; +} +#endif + + +/* + * netflt + */ +#ifndef VBOXNETADP +static NDIS_STATUS vboxNetFltWinSynchNdisRequest(PVBOXNETFLTINS pNetFlt, PNDIS_REQUEST pRequest) +{ + int rc; + + Assert(KeGetCurrentIrql() < DISPATCH_LEVEL); + + /* 1. serialize */ + rc = RTSemFastMutexRequest(pNetFlt->u.s.WinIf.hSynchRequestMutex); AssertRC(rc); + if (RT_SUCCESS(rc)) + { + NDIS_STATUS fRequestStatus = NDIS_STATUS_SUCCESS; + + /* 2. set pNetFlt->u.s.pSynchRequest */ + Assert(!pNetFlt->u.s.WinIf.pSynchRequest); + pNetFlt->u.s.WinIf.pSynchRequest = pRequest; + + /* 3. call NdisRequest */ + NdisRequest(&fRequestStatus, pNetFlt->u.s.WinIf.hBinding, pRequest); + + if (fRequestStatus == NDIS_STATUS_PENDING) + { + /* 3.1 if pending wait and assign the resulting status */ + KeWaitForSingleObject(&pNetFlt->u.s.WinIf.hSynchCompletionEvent, Executive, + KernelMode, FALSE, NULL); + + fRequestStatus = pNetFlt->u.s.WinIf.SynchCompletionStatus; + } + + /* 4. clear the pNetFlt->u.s.pSynchRequest */ + pNetFlt->u.s.WinIf.pSynchRequest = NULL; + + RTSemFastMutexRelease(pNetFlt->u.s.WinIf.hSynchRequestMutex); AssertRC(rc); + return fRequestStatus; + } + return NDIS_STATUS_FAILURE; +} + + +DECLHIDDEN(NDIS_STATUS) vboxNetFltWinGetMacAddress(PVBOXNETFLTINS pNetFlt, PRTMAC pMac) +{ + NDIS_REQUEST request; + NDIS_STATUS status; + request.RequestType = NdisRequestQueryInformation; + request.DATA.QUERY_INFORMATION.InformationBuffer = pMac; + request.DATA.QUERY_INFORMATION.InformationBufferLength = sizeof(RTMAC); + request.DATA.QUERY_INFORMATION.Oid = OID_802_3_CURRENT_ADDRESS; + status = vboxNetFltWinSynchNdisRequest(pNetFlt, &request); + if (status != NDIS_STATUS_SUCCESS) + { + /** @todo */ + AssertFailed(); + } + + return status; + +} + +DECLHIDDEN(NDIS_STATUS) vboxNetFltWinQueryPhysicalMedium(PVBOXNETFLTINS pNetFlt, NDIS_PHYSICAL_MEDIUM * pMedium) +{ + NDIS_REQUEST Request; + NDIS_STATUS Status; + Request.RequestType = NdisRequestQueryInformation; + Request.DATA.QUERY_INFORMATION.InformationBuffer = pMedium; + Request.DATA.QUERY_INFORMATION.InformationBufferLength = sizeof(NDIS_PHYSICAL_MEDIUM); + Request.DATA.QUERY_INFORMATION.Oid = OID_GEN_PHYSICAL_MEDIUM; + Status = vboxNetFltWinSynchNdisRequest(pNetFlt, &Request); + if (Status != NDIS_STATUS_SUCCESS) + { + if (Status == NDIS_STATUS_NOT_SUPPORTED || Status == NDIS_STATUS_NOT_RECOGNIZED || Status == NDIS_STATUS_INVALID_OID) + { + Status = NDIS_STATUS_NOT_SUPPORTED; + } + else + { + LogRel(("OID_GEN_PHYSICAL_MEDIUM failed: Status (0x%x)", Status)); + AssertFailed(); + } + } + return Status; +} + +DECLHIDDEN(bool) vboxNetFltWinIsPromiscuous(PVBOXNETFLTINS pNetFlt) +{ + /** @todo r=bird: This is too slow and is probably returning the wrong + * information. What we're interested in is whether someone besides us + * has put the interface into promiscuous mode. */ + NDIS_REQUEST request; + NDIS_STATUS status; + ULONG filter; + Assert(VBOXNETFLT_PROMISCUOUS_SUPPORTED(pNetFlt)); + request.RequestType = NdisRequestQueryInformation; + request.DATA.QUERY_INFORMATION.InformationBuffer = &filter; + request.DATA.QUERY_INFORMATION.InformationBufferLength = sizeof(filter); + request.DATA.QUERY_INFORMATION.Oid = OID_GEN_CURRENT_PACKET_FILTER; + status = vboxNetFltWinSynchNdisRequest(pNetFlt, &request); + if (status != NDIS_STATUS_SUCCESS) + { + /** @todo */ + AssertFailed(); + return false; + } + return (filter & NDIS_PACKET_TYPE_PROMISCUOUS) != 0; +} + +DECLHIDDEN(NDIS_STATUS) vboxNetFltWinSetPromiscuous(PVBOXNETFLTINS pNetFlt, bool bYes) +{ +/** @todo Need to report changes to the switch via: + * pThis->pSwitchPort->pfnReportPromiscuousMode(pThis->pSwitchPort, fPromisc); + */ + Assert(VBOXNETFLT_PROMISCUOUS_SUPPORTED(pNetFlt)); + if (VBOXNETFLT_PROMISCUOUS_SUPPORTED(pNetFlt)) + { + NDIS_REQUEST Request; + NDIS_STATUS fStatus; + ULONG fFilter; + ULONG fExpectedFilter; + ULONG fOurFilter; + Request.RequestType = NdisRequestQueryInformation; + Request.DATA.QUERY_INFORMATION.InformationBuffer = &fFilter; + Request.DATA.QUERY_INFORMATION.InformationBufferLength = sizeof(fFilter); + Request.DATA.QUERY_INFORMATION.Oid = OID_GEN_CURRENT_PACKET_FILTER; + fStatus = vboxNetFltWinSynchNdisRequest(pNetFlt, &Request); + if (fStatus != NDIS_STATUS_SUCCESS) + { + /** @todo */ + AssertFailed(); + return fStatus; + } + + if (!pNetFlt->u.s.WinIf.StateFlags.fUpperProtSetFilterInitialized) + { + /* the cache was not initialized yet, initiate it with the current filter value */ + pNetFlt->u.s.WinIf.fUpperProtocolSetFilter = fFilter; + pNetFlt->u.s.WinIf.StateFlags.fUpperProtSetFilterInitialized = TRUE; + } + + + if (bYes) + { + fExpectedFilter = NDIS_PACKET_TYPE_PROMISCUOUS; + fOurFilter = NDIS_PACKET_TYPE_PROMISCUOUS; + } + else + { + fExpectedFilter = pNetFlt->u.s.WinIf.fUpperProtocolSetFilter; + fOurFilter = 0; + } + + if (fExpectedFilter != fFilter) + { + Request.RequestType = NdisRequestSetInformation; + Request.DATA.SET_INFORMATION.InformationBuffer = &fExpectedFilter; + Request.DATA.SET_INFORMATION.InformationBufferLength = sizeof(fExpectedFilter); + Request.DATA.SET_INFORMATION.Oid = OID_GEN_CURRENT_PACKET_FILTER; + fStatus = vboxNetFltWinSynchNdisRequest(pNetFlt, &Request); + if (fStatus != NDIS_STATUS_SUCCESS) + { + /** @todo */ + AssertFailed(); + return fStatus; + } + } + pNetFlt->u.s.WinIf.fOurSetFilter = fOurFilter; + return fStatus; + } + return NDIS_STATUS_NOT_SUPPORTED; +} + +#else /* VBOXNETADP */ + +/** + * Generates a new unique MAC address based on our vendor ID + */ +DECLHIDDEN(void) vboxNetFltWinGenerateMACAddress(RTMAC *pMac) +{ + /* temporary use a time info */ + uint64_t NanoTS = RTTimeSystemNanoTS(); + pMac->au8[0] = (uint8_t)((VBOXNETADP_VENDOR_ID >> 16) & 0xff); + pMac->au8[1] = (uint8_t)((VBOXNETADP_VENDOR_ID >> 8) & 0xff); + pMac->au8[2] = (uint8_t)(VBOXNETADP_VENDOR_ID & 0xff); + pMac->au8[3] = (uint8_t)(NanoTS & 0xff0000); + pMac->au16[2] = (uint16_t)(NanoTS & 0xffff); +} + +DECLHIDDEN(int) vboxNetFltWinMAC2NdisString(RTMAC *pMac, PNDIS_STRING pNdisString) +{ + static const char s_achDigits[17] = "0123456789abcdef"; + PWSTR pString; + + /* validate parameters */ + AssertPtrReturn(pMac, VERR_INVALID_PARAMETER); + AssertPtrReturn(pNdisString, VERR_INVALID_PARAMETER); + AssertReturn(pNdisString->MaximumLength >= 13*sizeof(pNdisString->Buffer[0]), VERR_INVALID_PARAMETER); + + pString = pNdisString->Buffer; + + for (int i = 0; i < 6; i++) + { + uint8_t u8 = pMac->au8[i]; + pString[0] = s_achDigits[(u8 >> 4) & 0xf]; + pString[1] = s_achDigits[(u8/*>>0*/)& 0xf]; + pString += 2; + } + + pNdisString->Length = 12*sizeof(pNdisString->Buffer[0]); + + *pString = L'\0'; + + return VINF_SUCCESS; +} + +static int vboxNetFltWinWchar2Byte(WCHAR c, uint8_t *pb) +{ + if (c >= L'A' && c <= L'F') + *pb = (c - L'A') + 10; + else if (c >= L'a' && c <= L'f') + *pb = (c - L'a') + 10; + else if (c >= L'0' && c <= L'9') + *pb = (c - L'0'); + else + return VERR_INVALID_PARAMETER; + return VINF_SUCCESS; +} + +DECLHIDDEN(int) vboxNetFltWinMACFromNdisString(RTMAC *pMac, PNDIS_STRING pNdisString) +{ + + /* validate parameters */ + AssertPtrReturn(pMac, VERR_INVALID_PARAMETER); + AssertPtrReturn(pNdisString, VERR_INVALID_PARAMETER); + AssertReturn(pNdisString->Length >= 12*sizeof(pNdisString->Buffer[0]), VERR_INVALID_PARAMETER); + + int rc = VINF_SUCCESS; + PWSTR pString = pNdisString->Buffer; + for (int i = 0; i < 6; i++) + { + uint8_t v1, v2; + rc = vboxNetFltWinWchar2Byte(pString[0], &v1); + if (RT_FAILURE(rc)) + break; + + rc = vboxNetFltWinWchar2Byte(pString[1], &v2); + if (RT_FAILURE(rc)) + break; + + pMac->au8[i] = (v1 << 4) | v2; + + pString += 2; + } + + return rc; +} + +#endif /* VBOXNETADP */ + +/** + * creates a NDIS_PACKET from the PINTNETSG + */ +DECLHIDDEN(PNDIS_PACKET) vboxNetFltWinNdisPacketFromSG(PVBOXNETFLTINS pNetFlt, PINTNETSG pSG, PVOID pBufToFree, bool bToWire, bool bCopyMemory) +{ + NDIS_STATUS fStatus; + PNDIS_PACKET pPacket; + + Assert(pSG->aSegs[0].pv); + Assert(pSG->cbTotal >= VBOXNETFLT_PACKET_ETHEADER_SIZE); + +/** @todo Hrmpf, how can we fix this assumption? I fear this'll cause data + * corruption and maybe even BSODs ... */ + AssertReturn(pSG->cSegsUsed == 1 || bCopyMemory, NULL); + +#ifdef VBOXNETADP + NdisAllocatePacket(&fStatus, &pPacket, pNetFlt->u.s.WinIf.hRecvPacketPool); +#else + NdisAllocatePacket(&fStatus, &pPacket, bToWire ? pNetFlt->u.s.WinIf.hSendPacketPool : pNetFlt->u.s.WinIf.hRecvPacketPool); +#endif + if (fStatus == NDIS_STATUS_SUCCESS) + { + PNDIS_BUFFER pBuffer; + PVOID pvMemBuf; + + /** @todo generally we do not always need to zero-initialize the complete OOB data here, reinitialize only when/what we need, + * however we DO need to reset the status for the packets we indicate via NdisMIndicateReceivePacket to avoid packet loss + * in case the status contains NDIS_STATUS_RESOURCES */ + VBOXNETFLT_OOB_INIT(pPacket); + + if (bCopyMemory) + { + fStatus = vboxNetFltWinMemAlloc(&pvMemBuf, pSG->cbTotal); + Assert(fStatus == NDIS_STATUS_SUCCESS); + if (fStatus == NDIS_STATUS_SUCCESS) + IntNetSgRead(pSG, pvMemBuf); + } + else + { + pvMemBuf = pSG->aSegs[0].pv; + } + if (fStatus == NDIS_STATUS_SUCCESS) + { +#ifdef VBOXNETADP + NdisAllocateBuffer(&fStatus, &pBuffer, + pNetFlt->u.s.WinIf.hRecvBufferPool, + pvMemBuf, + pSG->cbTotal); +#else + NdisAllocateBuffer(&fStatus, &pBuffer, + bToWire ? pNetFlt->u.s.WinIf.hSendBufferPool : pNetFlt->u.s.WinIf.hRecvBufferPool, + pvMemBuf, + pSG->cbTotal); +#endif + + if (fStatus == NDIS_STATUS_SUCCESS) + { + NdisChainBufferAtBack(pPacket, pBuffer); + + if (bToWire) + { + PVBOXNETFLT_PKTRSVD_PT pSendInfo = (PVBOXNETFLT_PKTRSVD_PT)pPacket->ProtocolReserved; + pSendInfo->pOrigPacket = NULL; + pSendInfo->pBufToFree = pBufToFree; +#ifdef VBOX_LOOPBACK_USEFLAGS + /* set "don't loopback" flags */ + NdisGetPacketFlags(pPacket) = g_VBoxNetFltGlobalsWin.fPacketDontLoopBack; +#else + NdisGetPacketFlags(pPacket) = 0; +#endif + } + else + { + PVBOXNETFLT_PKTRSVD_MP pRecvInfo = (PVBOXNETFLT_PKTRSVD_MP)pPacket->MiniportReserved; + pRecvInfo->pOrigPacket = NULL; + pRecvInfo->pBufToFree = pBufToFree; + + /* we must set the header size on receive */ + NDIS_SET_PACKET_HEADER_SIZE(pPacket, VBOXNETFLT_PACKET_ETHEADER_SIZE); + /* NdisAllocatePacket zero-initializes the OOB data, + * but keeps the packet flags, clean them here */ + NdisGetPacketFlags(pPacket) = 0; + } + /** @todo set out of bound data */ + } + else + { + AssertFailed(); + if (bCopyMemory) + { + vboxNetFltWinMemFree(pvMemBuf); + } + NdisFreePacket(pPacket); + pPacket = NULL; + } + } + else + { + AssertFailed(); + NdisFreePacket(pPacket); + pPacket = NULL; + } + } + else + { + pPacket = NULL; + } + + DBG_CHECK_PACKET_AND_SG(pPacket, pSG); + + return pPacket; +} + +/* + * frees NDIS_PACKET created with vboxNetFltWinNdisPacketFromSG + */ +DECLHIDDEN(void) vboxNetFltWinFreeSGNdisPacket(PNDIS_PACKET pPacket, bool bFreeMem) +{ + UINT cBufCount; + PNDIS_BUFFER pFirstBuffer; + UINT uTotalPacketLength; + PNDIS_BUFFER pBuffer; + + NdisQueryPacket(pPacket, NULL, &cBufCount, &pFirstBuffer, &uTotalPacketLength); + + Assert(cBufCount == 1); + + do + { + NdisUnchainBufferAtBack(pPacket, &pBuffer); + if (pBuffer != NULL) + { + PVOID pvMemBuf; + UINT cbLength; + + NdisQueryBufferSafe(pBuffer, &pvMemBuf, &cbLength, NormalPagePriority); + NdisFreeBuffer(pBuffer); + if (bFreeMem) + { + vboxNetFltWinMemFree(pvMemBuf); + } + } + else + { + break; + } + } while (true); + + NdisFreePacket(pPacket); +} + +#if !defined(VBOXNETADP) +static void vboxNetFltWinAssociateMiniportProtocol(PVBOXNETFLTGLOBALS_WIN pGlobalsWin) +{ + NdisIMAssociateMiniport(pGlobalsWin->Mp.hMiniport, pGlobalsWin->Pt.hProtocol); +} +#endif + +/* + * NetFlt driver unload function + */ +DECLHIDDEN(VOID) vboxNetFltWinUnload(IN PDRIVER_OBJECT DriverObject) +{ + int rc; + UNREFERENCED_PARAMETER(DriverObject); + + LogFlowFunc(("ENTER: DO (0x%x)\n", DriverObject)); + + rc = vboxNetFltWinFiniIdc(); + if (RT_FAILURE(rc)) + { + /** @todo we can not prevent driver unload here */ + AssertFailed(); + + LogFlowFunc(("vboxNetFltWinFiniIdc - failed, busy.\n")); + } + + vboxNetFltWinJobFiniQueue(&g_VBoxJobQueue); +#ifndef VBOXNETADP + vboxNetFltWinPtDeregister(&g_VBoxNetFltGlobalsWin.Pt); +#endif + + vboxNetFltWinMpDeregister(&g_VBoxNetFltGlobalsWin.Mp); + +#ifndef VBOXNETADP + NdisFreeSpinLock(&g_VBoxNetFltGlobalsWin.lockFilters); +#endif /* VBOXNETADP */ + + LogFlow(("LEAVE: DO (0x%x)\n", DriverObject)); + + vboxNetFltWinFiniNetFltBase(); + /* don't use logging or any RT after de-init */ +} + +RT_C_DECLS_BEGIN + +NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath); + +RT_C_DECLS_END + +NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath) +{ + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + int rc; + + /* the idc registration is initiated via IOCTL since our driver + * can be loaded when the VBoxDrv is not in case we are a Ndis IM driver */ + rc = vboxNetFltWinInitNetFltBase(); + AssertRC(rc); + if (RT_SUCCESS(rc)) + { + Status = vboxNetFltWinJobInitQueue(&g_VBoxJobQueue); + Assert(Status == STATUS_SUCCESS); + if (Status == STATUS_SUCCESS) + { + ULONG MjVersion; + ULONG MnVersion; + + /* note: we do it after we initialize the Job Queue */ + vboxNetFltWinStartInitIdcProbing(); + + NdisZeroMemory(&g_VBoxNetFltGlobalsWin, sizeof (g_VBoxNetFltGlobalsWin)); + KeInitializeEvent(&g_VBoxNetFltGlobalsWin.SynchEvent, SynchronizationEvent, TRUE /* signalled*/); + + PsGetVersion(&MjVersion, &MnVersion, + NULL, /* PULONG BuildNumber OPTIONAL */ + NULL /* PUNICODE_STRING CSDVersion OPTIONAL */ + ); + + g_VBoxNetFltGlobalsWin.fPacketDontLoopBack = NDIS_FLAGS_DONT_LOOPBACK; + + if (MjVersion == 5 && MnVersion == 0) + { + /* this is Win2k, we don't support it actually, but just in case */ + g_VBoxNetFltGlobalsWin.fPacketDontLoopBack |= NDIS_FLAGS_SKIP_LOOPBACK_W2K; + } + + g_VBoxNetFltGlobalsWin.fPacketIsLoopedBack = NDIS_FLAGS_IS_LOOPBACK_PACKET; + +#ifndef VBOXNETADP + RTListInit(&g_VBoxNetFltGlobalsWin.listFilters); + NdisAllocateSpinLock(&g_VBoxNetFltGlobalsWin.lockFilters); +#endif + + Status = vboxNetFltWinMpRegister(&g_VBoxNetFltGlobalsWin.Mp, DriverObject, RegistryPath); + Assert(Status == STATUS_SUCCESS); + if (Status == NDIS_STATUS_SUCCESS) + { +#ifndef VBOXNETADP + Status = vboxNetFltWinPtRegister(&g_VBoxNetFltGlobalsWin.Pt, DriverObject, RegistryPath); + Assert(Status == STATUS_SUCCESS); + if (Status == NDIS_STATUS_SUCCESS) +#endif + { +#ifndef VBOXNETADP + vboxNetFltWinAssociateMiniportProtocol(&g_VBoxNetFltGlobalsWin); +#endif + return STATUS_SUCCESS; + +//#ifndef VBOXNETADP +// vboxNetFltWinPtDeregister(&g_VBoxNetFltGlobalsWin.Pt); +//#endif + } +#ifndef VBOXNETADP /* unreachable for VBOXNETADP because of the above return */ + vboxNetFltWinMpDeregister(&g_VBoxNetFltGlobalsWin.Mp); +# ifndef VBOXNETADP + NdisFreeSpinLock(&g_VBoxNetFltGlobalsWin.lockFilters); +# endif +#endif + } + vboxNetFltWinJobFiniQueue(&g_VBoxJobQueue); + } + vboxNetFltWinFiniNetFlt(); + } + else + { + Status = NDIS_STATUS_FAILURE; + } + + return Status; +} + +#ifndef VBOXNETADP +/** + * creates and initializes the packet to be sent to the underlying miniport given a packet posted to our miniport edge + * according to DDK docs we must create our own packet rather than posting the one passed to us + */ +DECLHIDDEN(NDIS_STATUS) vboxNetFltWinPrepareSendPacket(PVBOXNETFLTINS pNetFlt, PNDIS_PACKET pPacket, PNDIS_PACKET *ppMyPacket) +{ + NDIS_STATUS Status; + + NdisAllocatePacket(&Status, ppMyPacket, pNetFlt->u.s.WinIf.hSendPacketPool); + + if (Status == NDIS_STATUS_SUCCESS) + { + PVBOXNETFLT_PKTRSVD_PT pSendInfo = (PVBOXNETFLT_PKTRSVD_PT)((*ppMyPacket)->ProtocolReserved); + pSendInfo->pOrigPacket = pPacket; + pSendInfo->pBufToFree = NULL; + /* the rest will be filled on send */ + + vboxNetFltWinCopyPacketInfoOnSend(*ppMyPacket, pPacket); + +#ifdef VBOX_LOOPBACK_USEFLAGS + NdisGetPacketFlags(*ppMyPacket) |= g_VBoxNetFltGlobalsWin.fPacketDontLoopBack; +#endif + } + else + { + *ppMyPacket = NULL; + } + + return Status; +} + +/** + * creates and initializes the packet to be sent to the upperlying protocol given a packet indicated to our protocol edge + * according to DDK docs we must create our own packet rather than posting the one passed to us + */ +DECLHIDDEN(NDIS_STATUS) vboxNetFltWinPrepareRecvPacket(PVBOXNETFLTINS pNetFlt, PNDIS_PACKET pPacket, PNDIS_PACKET *ppMyPacket, bool bDpr) +{ + NDIS_STATUS Status; + + if (bDpr) + { + Assert(KeGetCurrentIrql() == DISPATCH_LEVEL); + NdisDprAllocatePacket(&Status, ppMyPacket, pNetFlt->u.s.WinIf.hRecvPacketPool); + } + else + { + NdisAllocatePacket(&Status, ppMyPacket, pNetFlt->u.s.WinIf.hRecvPacketPool); + } + + if (Status == NDIS_STATUS_SUCCESS) + { + PVBOXNETFLT_PKTRSVD_MP pRecvInfo = (PVBOXNETFLT_PKTRSVD_MP)((*ppMyPacket)->MiniportReserved); + pRecvInfo->pOrigPacket = pPacket; + pRecvInfo->pBufToFree = NULL; + + Status = vboxNetFltWinCopyPacketInfoOnRecv(*ppMyPacket, pPacket, false); + } + else + { + *ppMyPacket = NULL; + } + return Status; +} +#endif +/** + * initializes the VBOXNETFLTINS (our context structure) and binds to the given adapter + */ +#if defined(VBOXNETADP) +DECLHIDDEN(NDIS_STATUS) vboxNetFltWinPtInitBind(PVBOXNETFLTINS *ppNetFlt, NDIS_HANDLE hMiniportAdapter, PNDIS_STRING pBindToMiniportName /* actually this is our miniport name*/, NDIS_HANDLE hWrapperConfigurationContext) +#else +DECLHIDDEN(NDIS_STATUS) vboxNetFltWinPtInitBind(PVBOXNETFLTINS *ppNetFlt, PNDIS_STRING pOurMiniportName, PNDIS_STRING pBindToMiniportName) +#endif +{ + NDIS_STATUS Status; + do + { + ANSI_STRING AnsiString; + int rc; + PVBOXNETFLTINS pInstance; + USHORT cbAnsiName = pBindToMiniportName->Length;/* the length is is bytes ; *2 ;RtlUnicodeStringToAnsiSize(pBindToMiniportName)*/ + CREATE_INSTANCE_CONTEXT Context; + +# ifndef VBOXNETADP + Context.pOurName = pOurMiniportName; + Context.pBindToName = pBindToMiniportName; +# else + Context.hMiniportAdapter = hMiniportAdapter; + Context.hWrapperConfigurationContext = hWrapperConfigurationContext; +# endif + Context.Status = NDIS_STATUS_SUCCESS; + + AnsiString.Buffer = 0; /* will be allocated by RtlUnicodeStringToAnsiString */ + AnsiString.Length = 0; + AnsiString.MaximumLength = cbAnsiName; + + Assert(KeGetCurrentIrql() == PASSIVE_LEVEL); + + Status = RtlUnicodeStringToAnsiString(&AnsiString, pBindToMiniportName, true); + + if (Status != STATUS_SUCCESS) + { + break; + } + + rc = vboxNetFltSearchCreateInstance(&g_VBoxNetFltGlobals, AnsiString.Buffer, &pInstance, &Context); + RtlFreeAnsiString(&AnsiString); + if (RT_FAILURE(rc)) + { + AssertFailed(); + Status = Context.Status != NDIS_STATUS_SUCCESS ? Context.Status : NDIS_STATUS_FAILURE; + break; + } + + Assert(pInstance); + + if (rc == VINF_ALREADY_INITIALIZED) + { + /* the case when our adapter was unbound while IntNet was connected to it */ + /* the instance remains valid until IntNet disconnects from it, we simply search and re-use it*/ + rc = vboxNetFltWinAttachToInterface(pInstance, &Context, true); + if (RT_FAILURE(rc)) + { + AssertFailed(); + Status = Context.Status != NDIS_STATUS_SUCCESS ? Context.Status : NDIS_STATUS_FAILURE; + /* release netflt */ + vboxNetFltRelease(pInstance, false); + + break; + } + } + + *ppNetFlt = pInstance; + + } while (FALSE); + + return Status; +} +/* + * deinitializes the VBOXNETFLTWIN + */ +DECLHIDDEN(VOID) vboxNetFltWinPtFiniWinIf(PVBOXNETFLTWIN pWinIf) +{ +#ifndef VBOXNETADP + int rc; +#endif + + LogFlowFunc(("ENTER: pWinIf 0x%p\n", pWinIf)); + + Assert(KeGetCurrentIrql() == PASSIVE_LEVEL); +#ifndef VBOXNETADP + if (pWinIf->MpDeviceName.Buffer) + { + vboxNetFltWinMemFree(pWinIf->MpDeviceName.Buffer); + } + + FINI_INTERLOCKED_SINGLE_LIST(&pWinIf->TransferDataList); +# if defined(DEBUG_NETFLT_LOOPBACK) || !defined(VBOX_LOOPBACK_USEFLAGS) + FINI_INTERLOCKED_SINGLE_LIST(&pWinIf->SendPacketQueue); +# endif + NdisFreeBufferPool(pWinIf->hSendBufferPool); + NdisFreePacketPool(pWinIf->hSendPacketPool); + rc = RTSemFastMutexDestroy(pWinIf->hSynchRequestMutex); AssertRC(rc); +#endif + + /* NOTE: NULL is a valid handle */ + NdisFreeBufferPool(pWinIf->hRecvBufferPool); + NdisFreePacketPool(pWinIf->hRecvPacketPool); + + LogFlowFunc(("LEAVE: pWinIf 0x%p\n", pWinIf)); +} + +#ifndef VBOXNETADP +DECLHIDDEN(NDIS_STATUS) vboxNetFltWinPtInitWinIf(PVBOXNETFLTWIN pWinIf, IN PNDIS_STRING pOurDeviceName) +#else +DECLHIDDEN(NDIS_STATUS) vboxNetFltWinPtInitWinIf(PVBOXNETFLTWIN pWinIf) +#endif +{ + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; +#ifndef VBOXNETADP + int rc; +#endif + + LogFlowFunc(("ENTER: pWinIf 0x%p\n", pWinIf)); + + Assert(KeGetCurrentIrql() == PASSIVE_LEVEL); + + NdisZeroMemory(pWinIf, sizeof (VBOXNETFLTWIN)); + NdisAllocatePacketPoolEx(&Status, &pWinIf->hRecvPacketPool, + VBOXNETFLT_PACKET_POOL_SIZE_NORMAL, + VBOXNETFLT_PACKET_POOL_SIZE_OVERFLOW, + PROTOCOL_RESERVED_SIZE_IN_PACKET); + Assert(Status == NDIS_STATUS_SUCCESS); + if (Status == NDIS_STATUS_SUCCESS) + { + /* NOTE: NULL is a valid handle !!! */ + NdisAllocateBufferPool(&Status, &pWinIf->hRecvBufferPool, VBOXNETFLT_BUFFER_POOL_SIZE_RX); + Assert(Status == NDIS_STATUS_SUCCESS); + if (Status == NDIS_STATUS_SUCCESS) + { + pWinIf->MpState.PowerState = NdisDeviceStateD3; + vboxNetFltWinSetOpState(&pWinIf->MpState, kVBoxNetDevOpState_Deinitialized); +#ifndef VBOXNETADP + pWinIf->PtState.PowerState = NdisDeviceStateD3; + vboxNetFltWinSetOpState(&pWinIf->PtState, kVBoxNetDevOpState_Deinitialized); + + NdisAllocateBufferPool(&Status, + &pWinIf->hSendBufferPool, + VBOXNETFLT_BUFFER_POOL_SIZE_TX); + Assert(Status == NDIS_STATUS_SUCCESS); + if (Status == NDIS_STATUS_SUCCESS) + { + INIT_INTERLOCKED_SINGLE_LIST(&pWinIf->TransferDataList); + +# if defined(DEBUG_NETFLT_LOOPBACK) || !defined(VBOX_LOOPBACK_USEFLAGS) + INIT_INTERLOCKED_SINGLE_LIST(&pWinIf->SendPacketQueue); +# endif + NdisInitializeEvent(&pWinIf->OpenCloseEvent); + + KeInitializeEvent(&pWinIf->hSynchCompletionEvent, SynchronizationEvent, FALSE); + + NdisInitializeEvent(&pWinIf->MpInitCompleteEvent); + + NdisAllocatePacketPoolEx(&Status, &pWinIf->hSendPacketPool, + VBOXNETFLT_PACKET_POOL_SIZE_NORMAL, + VBOXNETFLT_PACKET_POOL_SIZE_OVERFLOW, + sizeof (PVBOXNETFLT_PKTRSVD_PT)); + Assert(Status == NDIS_STATUS_SUCCESS); + if (Status == NDIS_STATUS_SUCCESS) + { + rc = RTSemFastMutexCreate(&pWinIf->hSynchRequestMutex); + AssertRC(rc); + if (RT_SUCCESS(rc)) + { + Status = vboxNetFltWinMemAlloc((PVOID*)&pWinIf->MpDeviceName.Buffer, pOurDeviceName->Length); + Assert(Status == NDIS_STATUS_SUCCESS); + if (Status == NDIS_STATUS_SUCCESS) + { + pWinIf->MpDeviceName.MaximumLength = pOurDeviceName->Length; + pWinIf->MpDeviceName.Length = 0; + Status = vboxNetFltWinCopyString(&pWinIf->MpDeviceName, pOurDeviceName); +#endif + return NDIS_STATUS_SUCCESS; +#ifndef VBOXNETADP + // unreachable: vboxNetFltWinMemFree(pWinIf->MpDeviceName.Buffer); + } + RTSemFastMutexDestroy(pWinIf->hSynchRequestMutex); + } + else + Status = NDIS_STATUS_FAILURE; + NdisFreePacketPool(pWinIf->hSendPacketPool); + } + NdisFreeBufferPool(pWinIf->hSendBufferPool); + } + NdisFreeBufferPool(pWinIf->hRecvBufferPool); +#endif + } + NdisFreePacketPool(pWinIf->hRecvPacketPool); + } + + LogFlowFunc(("LEAVE: pWinIf 0x%p, Status 0x%x\n", pWinIf, Status)); + + return Status; +} + +/** + * match packets + */ +#define NEXT_LIST_ENTRY(_Entry) ((_Entry)->Flink) +#define PREV_LIST_ENTRY(_Entry) ((_Entry)->Blink) +#define FIRST_LIST_ENTRY NEXT_LIST_ENTRY +#define LAST_LIST_ENTRY PREV_LIST_ENTRY + +#define MIN(_a, _b) ((_a) < (_b) ? (_a) : (_b)) + +#ifndef VBOXNETADP + +#ifdef DEBUG_misha + +RTMAC g_vboxNetFltWinVerifyMACBroadcast = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; +RTMAC g_vboxNetFltWinVerifyMACGuest = {0x08, 0x00, 0x27, 0x01, 0x02, 0x03}; + +DECLHIDDEN(PRTNETETHERHDR) vboxNetFltWinGetEthHdr(PNDIS_PACKET pPacket) +{ + UINT cBufCount1; + PNDIS_BUFFER pBuffer1; + UINT uTotalPacketLength1; + RTNETETHERHDR* pEth; + UINT cbLength1 = 0; + UINT i = 0; + + NdisQueryPacket(pPacket, NULL, &cBufCount1, &pBuffer1, &uTotalPacketLength1); + + Assert(pBuffer1); + Assert(uTotalPacketLength1 >= VBOXNETFLT_PACKET_ETHEADER_SIZE); + if (uTotalPacketLength1 < VBOXNETFLT_PACKET_ETHEADER_SIZE) + return NULL; + + NdisQueryBufferSafe(pBuffer1, &pEth, &cbLength1, NormalPagePriority); + Assert(cbLength1 >= VBOXNETFLT_PACKET_ETHEADER_SIZE); + if (cbLength1 < VBOXNETFLT_PACKET_ETHEADER_SIZE) + return NULL; + + return pEth; +} + +DECLHIDDEN(PRTNETETHERHDR) vboxNetFltWinGetEthHdrSG(PINTNETSG pSG) +{ + Assert(pSG->cSegsUsed); + Assert(pSG->cSegsAlloc >= pSG->cSegsUsed); + Assert(pSG->aSegs[0].cb >= VBOXNETFLT_PACKET_ETHEADER_SIZE); + + if (!pSG->cSegsUsed) + return NULL; + + if (pSG->aSegs[0].cb < VBOXNETFLT_PACKET_ETHEADER_SIZE) + return NULL; + + return (PRTNETETHERHDR)pSG->aSegs[0].pv; +} + +DECLHIDDEN(bool) vboxNetFltWinCheckMACs(PNDIS_PACKET pPacket, PRTMAC pDst, PRTMAC pSrc) +{ + PRTNETETHERHDR pHdr = vboxNetFltWinGetEthHdr(pPacket); + Assert(pHdr); + + if (!pHdr) + return false; + + if (pDst && memcmp(pDst, &pHdr->DstMac, sizeof(RTMAC))) + return false; + + if (pSrc && memcmp(pSrc, &pHdr->SrcMac, sizeof(RTMAC))) + return false; + + return true; +} + +DECLHIDDEN(bool) vboxNetFltWinCheckMACsSG(PINTNETSG pSG, PRTMAC pDst, PRTMAC pSrc) +{ + PRTNETETHERHDR pHdr = vboxNetFltWinGetEthHdrSG(pSG); + Assert(pHdr); + + if (!pHdr) + return false; + + if (pDst && memcmp(pDst, &pHdr->DstMac, sizeof(RTMAC))) + return false; + + if (pSrc && memcmp(pSrc, &pHdr->SrcMac, sizeof(RTMAC))) + return false; + + return true; +} +#endif + +# if !defined(VBOX_LOOPBACK_USEFLAGS) || defined(DEBUG_NETFLT_PACKETS) +/* + * answers whether the two given packets match based on the packet length and the first cbMatch bytes of the packets + * if cbMatch < 0 matches complete packets. + */ +DECLHIDDEN(bool) vboxNetFltWinMatchPackets(PNDIS_PACKET pPacket1, PNDIS_PACKET pPacket2, const INT cbMatch) +{ + UINT cBufCount1; + PNDIS_BUFFER pBuffer1; + UINT uTotalPacketLength1; + uint8_t *pbMemBuf1 = NULL; + UINT cbLength1 = 0; + + UINT cBufCount2; + PNDIS_BUFFER pBuffer2; + UINT uTotalPacketLength2; + uint8_t *pbMemBuf2 = NULL; + UINT cbLength2 = 0; + bool bMatch = true; + +#ifdef DEBUG_NETFLT_PACKETS + bool bCompleteMatch = false; +#endif + + NdisQueryPacket(pPacket1, NULL, &cBufCount1, &pBuffer1, &uTotalPacketLength1); + NdisQueryPacket(pPacket2, NULL, &cBufCount2, &pBuffer2, &uTotalPacketLength2); + + Assert(pBuffer1); + Assert(pBuffer2); + + if (uTotalPacketLength1 != uTotalPacketLength2) + { + bMatch = false; + } + else + { + UINT ucbLength2Match = 0; + UINT ucbMatch; + if (cbMatch < 0 || (UINT)cbMatch > uTotalPacketLength1) + { + /* NOTE: assuming uTotalPacketLength1 == uTotalPacketLength2*/ + ucbMatch = uTotalPacketLength1; +#ifdef DEBUG_NETFLT_PACKETS + bCompleteMatch = true; +#endif + } + else + { + ucbMatch = (UINT)cbMatch; + } + + for (;;) + { + if (!cbLength1) + { + NdisQueryBufferSafe(pBuffer1, &pbMemBuf1, &cbLength1, NormalPagePriority); + NdisGetNextBuffer(pBuffer1, &pBuffer1); + } + else + { + Assert(pbMemBuf1); + Assert(ucbLength2Match); + pbMemBuf1 += ucbLength2Match; + } + + if (!cbLength2) + { + NdisQueryBufferSafe(pBuffer2, &pbMemBuf2, &cbLength2, NormalPagePriority); + NdisGetNextBuffer(pBuffer2, &pBuffer2); + } + else + { + Assert(pbMemBuf2); + Assert(ucbLength2Match); + pbMemBuf2 += ucbLength2Match; + } + + ucbLength2Match = MIN(ucbMatch, cbLength1); + ucbLength2Match = MIN(ucbLength2Match, cbLength2); + + if (memcmp(pbMemBuf1, pbMemBuf2, ucbLength2Match)) + { + bMatch = false; + break; + } + + ucbMatch -= ucbLength2Match; + if (!ucbMatch) + break; + + cbLength1 -= ucbLength2Match; + cbLength2 -= ucbLength2Match; + } + } + +#ifdef DEBUG_NETFLT_PACKETS + if (bMatch && !bCompleteMatch) + { + /* check that the packets fully match */ + DBG_CHECK_PACKETS(pPacket1, pPacket2); + } +#endif + + return bMatch; +} + +/* + * answers whether the ndis packet and PINTNETSG match based on the packet length and the first cbMatch bytes of the packet and PINTNETSG + * if cbMatch < 0 matches complete packets. + */ +DECLHIDDEN(bool) vboxNetFltWinMatchPacketAndSG(PNDIS_PACKET pPacket, PINTNETSG pSG, const INT cbMatch) +{ + UINT cBufCount1; + PNDIS_BUFFER pBuffer1; + UINT uTotalPacketLength1; + uint8_t *pbMemBuf1 = NULL; + UINT cbLength1 = 0; + UINT uTotalPacketLength2 = pSG->cbTotal; + uint8_t *pbMemBuf2 = NULL; + UINT cbLength2 = 0; + bool bMatch = true; + bool bCompleteMatch = false; + UINT i = 0; + + NdisQueryPacket(pPacket, NULL, &cBufCount1, &pBuffer1, &uTotalPacketLength1); + + Assert(pBuffer1); + Assert(pSG->cSegsUsed); + Assert(pSG->cSegsAlloc >= pSG->cSegsUsed); + + if (uTotalPacketLength1 != uTotalPacketLength2) + { + AssertFailed(); + bMatch = false; + } + else + { + UINT ucbLength2Match = 0; + UINT ucbMatch; + + if (cbMatch < 0 || (UINT)cbMatch > uTotalPacketLength1) + { + /* NOTE: assuming uTotalPacketLength1 == uTotalPacketLength2*/ + ucbMatch = uTotalPacketLength1; + bCompleteMatch = true; + } + else + { + ucbMatch = (UINT)cbMatch; + } + + for (;;) + { + if (!cbLength1) + { + NdisQueryBufferSafe(pBuffer1, &pbMemBuf1, &cbLength1, NormalPagePriority); + NdisGetNextBuffer(pBuffer1, &pBuffer1); + } + else + { + Assert(pbMemBuf1); + Assert(ucbLength2Match); + pbMemBuf1 += ucbLength2Match; + } + + if (!cbLength2) + { + Assert(i < pSG->cSegsUsed); + pbMemBuf2 = (uint8_t*)pSG->aSegs[i].pv; + cbLength2 = pSG->aSegs[i].cb; + i++; + } + else + { + Assert(pbMemBuf2); + Assert(ucbLength2Match); + pbMemBuf2 += ucbLength2Match; + } + + ucbLength2Match = MIN(ucbMatch, cbLength1); + ucbLength2Match = MIN(ucbLength2Match, cbLength2); + + if (memcmp(pbMemBuf1, pbMemBuf2, ucbLength2Match)) + { + bMatch = false; + AssertFailed(); + break; + } + + ucbMatch -= ucbLength2Match; + if (!ucbMatch) + break; + + cbLength1 -= ucbLength2Match; + cbLength2 -= ucbLength2Match; + } + } + + if (bMatch && !bCompleteMatch) + { + /* check that the packets fully match */ + DBG_CHECK_PACKET_AND_SG(pPacket, pSG); + } + return bMatch; +} + +# if 0 +/* + * answers whether the two PINTNETSGs match based on the packet length and the first cbMatch bytes of the PINTNETSG + * if cbMatch < 0 matches complete packets. + */ +static bool vboxNetFltWinMatchSGs(PINTNETSG pSG1, PINTNETSG pSG2, const INT cbMatch) +{ + UINT uTotalPacketLength1 = pSG1->cbTotal; + PVOID pbMemBuf1 = NULL; + UINT cbLength1 = 0; + UINT i1 = 0; + UINT uTotalPacketLength2 = pSG2->cbTotal; + PVOID pbMemBuf2 = NULL; + UINT cbLength2 = 0; + + bool bMatch = true; + bool bCompleteMatch = false; + UINT i2 = 0; + + Assert(pSG1->cSegsUsed); + Assert(pSG2->cSegsUsed); + Assert(pSG1->cSegsAlloc >= pSG1->cSegsUsed); + Assert(pSG2->cSegsAlloc >= pSG2->cSegsUsed); + + if (uTotalPacketLength1 != uTotalPacketLength2) + { + AssertFailed(); + bMatch = false; + } + else + { + UINT ucbMatch; + if (cbMatch < 0 || (UINT)cbMatch > uTotalPacketLength1) + { + /* NOTE: assuming uTotalPacketLength1 == uTotalPacketLength2*/ + ucbMatch = uTotalPacketLength1; + bCompleteMatch = true; + } + else + { + ucbMatch = (UINT)cbMatch; + } + + do + { + UINT ucbLength2Match; + if (!cbLength1) + { + Assert(i1 < pSG1->cSegsUsed); + pbMemBuf1 = pSG1->aSegs[i1].pv; + cbLength1 = pSG1->aSegs[i1].cb; + i1++; + } + + if (!cbLength2) + { + Assert(i2 < pSG2->cSegsUsed); + pbMemBuf2 = pSG2->aSegs[i2].pv; + cbLength2 = pSG2->aSegs[i2].cb; + i2++; + } + + ucbLength2Match = MIN(ucbMatch, cbLength1); + ucbLength2Match = MIN(ucbLength2Match, cbLength2); + + if (memcmp(pbMemBuf1, pbMemBuf2, ucbLength2Match)) + { + bMatch = false; + AssertFailed(); + break; + } + ucbMatch -= ucbLength2Match; + cbLength1 -= ucbLength2Match; + cbLength2 -= ucbLength2Match; + } while (ucbMatch); + } + + if (bMatch && !bCompleteMatch) + { + /* check that the packets fully match */ + DBG_CHECK_SGS(pSG1, pSG2); + } + return bMatch; +} +# endif +# endif +#endif + +static void vboxNetFltWinFiniNetFltBase() +{ + do + { + vboxNetFltDeleteGlobals(&g_VBoxNetFltGlobals); + + /* + * Undo the work done during start (in reverse order). + */ + memset(&g_VBoxNetFltGlobals, 0, sizeof(g_VBoxNetFltGlobals)); + + RTLogDestroy(RTLogRelSetDefaultInstance(NULL)); + RTLogDestroy(RTLogSetDefaultInstance(NULL)); + + RTR0Term(); + } while (0); +} + +/* + * Defines max timeout for waiting for driver unloading + * (3000 * 100 ms = 5 minutes) + */ +#define MAX_UNLOAD_PROBES 3000 + +static int vboxNetFltWinFiniIdc() +{ + int rc; + int i; + + vboxNetFltWinStopInitIdcProbing(); + + if (g_bVBoxIdcInitialized) + { + for (i = 0; (rc = vboxNetFltTryDeleteIdc(&g_VBoxNetFltGlobals)) == VERR_WRONG_ORDER + && i < MAX_UNLOAD_PROBES; i++) + { + RTThreadSleep(100); + } + if (i == MAX_UNLOAD_PROBES) + { + // seems something hungs in driver + LogFlow(("vboxNetFltWinFiniIdc - Can't delete Idc. pInH=%p cFRefs=%d fIDcOpen=%s", + g_VBoxNetFltGlobals.pInstanceHead, g_VBoxNetFltGlobals.cFactoryRefs, + g_VBoxNetFltGlobals.fIDCOpen ? "true" : "false")); + LogFlow(("vboxNetFltWinFiniIdc g_VBoxNetFltGlobalsWin cDvRefs=%d hDev=%x pDev=%p Mp=%x \n", + g_VBoxNetFltGlobalsWin.cDeviceRefs, g_VBoxNetFltGlobalsWin.hDevice, + g_VBoxNetFltGlobalsWin.pDevObj, g_VBoxNetFltGlobalsWin.Mp.hMiniport)); + Assert(i == MAX_UNLOAD_PROBES); + return VERR_WRONG_ORDER; + } + + if (RT_SUCCESS(rc)) + { + g_bVBoxIdcInitialized = false; + } + } + else + { + rc = VINF_SUCCESS; + } + return rc; + +} + +static int vboxNetFltWinFiniNetFlt() +{ + int rc = vboxNetFltWinFiniIdc(); + if (RT_SUCCESS(rc)) + { + vboxNetFltWinFiniNetFltBase(); + } + return rc; +} + +/** + * base netflt initialization + */ +static int vboxNetFltWinInitNetFltBase() +{ + int rc; + + do + { + Assert(!g_bVBoxIdcInitialized); + + rc = RTR0Init(0); + if (!RT_SUCCESS(rc)) + { + break; + } + + memset(&g_VBoxNetFltGlobals, 0, sizeof(g_VBoxNetFltGlobals)); + rc = vboxNetFltInitGlobals(&g_VBoxNetFltGlobals); + if (!RT_SUCCESS(rc)) + { + RTR0Term(); + break; + } + }while (0); + + return rc; +} + +/** + * initialize IDC + */ +static int vboxNetFltWinInitIdc() +{ + int rc; + + do + { + if (g_bVBoxIdcInitialized) + { + rc = VINF_ALREADY_INITIALIZED; + break; + } + + /* + * connect to the support driver. + * + * This will call back vboxNetFltOsOpenSupDrv (and maybe vboxNetFltOsCloseSupDrv) + * for establishing the connect to the support driver. + */ + rc = vboxNetFltInitIdc(&g_VBoxNetFltGlobals); + if (!RT_SUCCESS(rc)) + { + break; + } + + g_bVBoxIdcInitialized = true; + } while (0); + + return rc; +} + +static VOID vboxNetFltWinInitIdcProbingWorker(PVOID pvContext) +{ + PINIT_IDC_INFO pInitIdcInfo = (PINIT_IDC_INFO)pvContext; + int rc = vboxNetFltWinInitIdc(); + if (RT_FAILURE(rc)) + { + bool bInterupted = ASMAtomicUoReadBool(&pInitIdcInfo->bStop); + if (!bInterupted) + { + RTThreadSleep(1000); /* 1 s */ + bInterupted = ASMAtomicUoReadBool(&pInitIdcInfo->bStop); + if (!bInterupted) + { + vboxNetFltWinJobEnqueueJob(&g_VBoxJobQueue, &pInitIdcInfo->Job, false); + return; + } + } + + /* it's interrupted */ + rc = VERR_INTERRUPTED; + } + + ASMAtomicUoWriteS32(&pInitIdcInfo->rc, rc); + KeSetEvent(&pInitIdcInfo->hCompletionEvent, 0, FALSE); +} + +static int vboxNetFltWinStopInitIdcProbing() +{ + if (!g_VBoxInitIdcInfo.bInitialized) + return VERR_INVALID_STATE; + + ASMAtomicUoWriteBool(&g_VBoxInitIdcInfo.bStop, true); + KeWaitForSingleObject(&g_VBoxInitIdcInfo.hCompletionEvent, Executive, KernelMode, FALSE, NULL); + + return g_VBoxInitIdcInfo.rc; +} + +static int vboxNetFltWinStartInitIdcProbing() +{ + Assert(!g_bVBoxIdcInitialized); + KeInitializeEvent(&g_VBoxInitIdcInfo.hCompletionEvent, NotificationEvent, FALSE); + g_VBoxInitIdcInfo.bStop = false; + g_VBoxInitIdcInfo.bInitialized = true; + vboxNetFltWinJobInit(&g_VBoxInitIdcInfo.Job, vboxNetFltWinInitIdcProbingWorker, &g_VBoxInitIdcInfo, false); + vboxNetFltWinJobEnqueueJob(&g_VBoxJobQueue, &g_VBoxInitIdcInfo.Job, false); + return VINF_SUCCESS; +} + +static int vboxNetFltWinInitNetFlt() +{ + int rc; + + do + { + rc = vboxNetFltWinInitNetFltBase(); + if (RT_FAILURE(rc)) + { + AssertFailed(); + break; + } + + /* + * connect to the support driver. + * + * This will call back vboxNetFltOsOpenSupDrv (and maybe vboxNetFltOsCloseSupDrv) + * for establishing the connect to the support driver. + */ + rc = vboxNetFltWinInitIdc(); + if (RT_FAILURE(rc)) + { + AssertFailed(); + vboxNetFltWinFiniNetFltBase(); + break; + } + } while (0); + + return rc; +} + +/* detach*/ +static int vboxNetFltWinDeleteInstance(PVBOXNETFLTINS pThis) +{ + LogFlow(("vboxNetFltWinDeleteInstance: pThis=0x%p \n", pThis)); + + Assert(KeGetCurrentIrql() < DISPATCH_LEVEL); + Assert(pThis); + Assert(pThis->fDisconnectedFromHost); + Assert(!pThis->fRediscoveryPending); + Assert(pThis->enmTrunkState != INTNETTRUNKIFSTATE_ACTIVE); +#ifndef VBOXNETADP + Assert(pThis->u.s.WinIf.PtState.OpState == kVBoxNetDevOpState_Deinitialized); + Assert(!pThis->u.s.WinIf.hBinding); +#endif + Assert(pThis->u.s.WinIf.MpState.OpState == kVBoxNetDevOpState_Deinitialized); +#ifndef VBOXNETFLT_NO_PACKET_QUEUE + Assert(!pThis->u.s.PacketQueueWorker.pSG); +#endif + + RTSemMutexDestroy(pThis->u.s.hWinIfMutex); + + vboxNetFltWinDrvDereference(); + + return VINF_SUCCESS; +} + +static NDIS_STATUS vboxNetFltWinDisconnectIt(PVBOXNETFLTINS pInstance) +{ +#ifndef VBOXNETFLT_NO_PACKET_QUEUE + vboxNetFltWinQuFiniPacketQueue(pInstance); +#else + RT_NOREF1(pInstance); +#endif + return NDIS_STATUS_SUCCESS; +} + +/* detach*/ +DECLHIDDEN(NDIS_STATUS) vboxNetFltWinDetachFromInterface(PVBOXNETFLTINS pNetFlt, bool bOnUnbind) +{ + NDIS_STATUS Status; + int rc; + LogFlowFunc(("ENTER: pThis=%0xp\n", pNetFlt)); + + Assert(KeGetCurrentIrql() < DISPATCH_LEVEL); + Assert(pNetFlt); + + /* paranoia to ensure the instance is not removed while we're waiting on the mutex + * in case ndis does something unpredictable, e.g. calls our miniport halt independently + * from protocol unbind and concurrently with it*/ + vboxNetFltRetain(pNetFlt, false); + + rc = RTSemMutexRequest(pNetFlt->u.s.hWinIfMutex, RT_INDEFINITE_WAIT); + if (RT_SUCCESS(rc)) + { + Assert(vboxNetFltWinGetWinIfState(pNetFlt) == kVBoxWinIfState_Connected); + Assert(vboxNetFltWinGetOpState(&pNetFlt->u.s.WinIf.MpState) == kVBoxNetDevOpState_Initialized); +#ifndef VBOXNETADP + Assert(vboxNetFltWinGetOpState(&pNetFlt->u.s.WinIf.PtState) == kVBoxNetDevOpState_Initialized); +#endif + if (vboxNetFltWinGetWinIfState(pNetFlt) == kVBoxWinIfState_Connected) + { + vboxNetFltWinSetWinIfState(pNetFlt, kVBoxWinIfState_Disconnecting); +#ifndef VBOXNETADP + Status = vboxNetFltWinPtDoUnbinding(pNetFlt, bOnUnbind); +#else + Status = vboxNetFltWinMpDoDeinitialization(pNetFlt); +#endif + Assert(Status == NDIS_STATUS_SUCCESS); + + vboxNetFltWinSetWinIfState(pNetFlt, kVBoxWinIfState_Disconnected); + Assert(vboxNetFltWinGetOpState(&pNetFlt->u.s.WinIf.MpState) == kVBoxNetDevOpState_Deinitialized); +#ifndef VBOXNETADP + Assert(vboxNetFltWinGetOpState(&pNetFlt->u.s.WinIf.PtState) == kVBoxNetDevOpState_Deinitialized); +#endif + vboxNetFltWinPtFiniWinIf(&pNetFlt->u.s.WinIf); + + /* we're unbinding, make an unbind-related release */ + vboxNetFltRelease(pNetFlt, false); + } + else + { + AssertBreakpoint(); +#ifndef VBOXNETADP + pNetFlt->u.s.WinIf.OpenCloseStatus = NDIS_STATUS_FAILURE; +#endif + if (!bOnUnbind) + { + vboxNetFltWinSetOpState(&pNetFlt->u.s.WinIf.MpState, kVBoxNetDevOpState_Deinitialized); + } + Status = NDIS_STATUS_FAILURE; + } + RTSemMutexRelease(pNetFlt->u.s.hWinIfMutex); + } + else + { + AssertBreakpoint(); + Status = NDIS_STATUS_FAILURE; + } + + /* release for the retain we made before waining on the mutex */ + vboxNetFltRelease(pNetFlt, false); + + LogFlowFunc(("LEAVE: Status 0x%x\n", Status)); + + return Status; +} + + +/** + * Checks if the host (not us) has put the adapter in promiscuous mode. + * + * @returns true if promiscuous, false if not. + * @param pThis The instance. + */ +static bool vboxNetFltWinIsPromiscuous2(PVBOXNETFLTINS pThis) +{ +#ifndef VBOXNETADP + if (VBOXNETFLT_PROMISCUOUS_SUPPORTED(pThis)) + { + bool bPromiscuous; + if (!vboxNetFltWinReferenceWinIf(pThis)) + return false; + + bPromiscuous = (pThis->u.s.WinIf.fUpperProtocolSetFilter & NDIS_PACKET_TYPE_PROMISCUOUS) == NDIS_PACKET_TYPE_PROMISCUOUS; + /*vboxNetFltWinIsPromiscuous(pAdapt);*/ + + vboxNetFltWinDereferenceWinIf(pThis); + return bPromiscuous; + } + return false; +#else + RT_NOREF1(pThis); + return true; +#endif +} + + +/** + * Report the MAC address, promiscuous mode setting, GSO capabilities and + * no-preempt destinations to the internal network. + * + * Does nothing if we're not currently connected to an internal network. + * + * @param pThis The instance data. + */ +static void vboxNetFltWinReportStuff(PVBOXNETFLTINS pThis) +{ + /** @todo Keep these up to date, esp. the promiscuous mode bit. */ + if (pThis->pSwitchPort + && vboxNetFltTryRetainBusyNotDisconnected(pThis)) + { + pThis->pSwitchPort->pfnReportMacAddress(pThis->pSwitchPort, &pThis->u.s.MacAddr); + pThis->pSwitchPort->pfnReportPromiscuousMode(pThis->pSwitchPort, + vboxNetFltWinIsPromiscuous2(pThis)); + pThis->pSwitchPort->pfnReportGsoCapabilities(pThis->pSwitchPort, 0, + INTNETTRUNKDIR_WIRE | INTNETTRUNKDIR_HOST); + /** @todo We should be able to do pfnXmit at DISPATCH_LEVEL... */ + pThis->pSwitchPort->pfnReportNoPreemptDsts(pThis->pSwitchPort, 0 /* none */); + vboxNetFltRelease(pThis, true /*fBusy*/); + } +} + +/** + * Worker for vboxNetFltWinAttachToInterface. + * + * @param pAttachInfo Structure for communicating with + * vboxNetFltWinAttachToInterface. + */ +static void vboxNetFltWinAttachToInterfaceWorker(PATTACH_INFO pAttachInfo) +{ + PVBOXNETFLTINS pThis = pAttachInfo->pNetFltIf; + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + int rc; + + Assert(KeGetCurrentIrql() == PASSIVE_LEVEL); + + /* to ensure we're not removed while we're here */ + vboxNetFltRetain(pThis, false); + + rc = RTSemMutexRequest(pThis->u.s.hWinIfMutex, RT_INDEFINITE_WAIT); + if (RT_SUCCESS(rc)) + { + Assert(vboxNetFltWinGetWinIfState(pThis) == kVBoxWinIfState_Disconnected); + Assert(vboxNetFltWinGetOpState(&pThis->u.s.WinIf.MpState) == kVBoxNetDevOpState_Deinitialized); +#ifndef VBOXNETADP + Assert(vboxNetFltWinGetOpState(&pThis->u.s.WinIf.PtState) == kVBoxNetDevOpState_Deinitialized); +#endif + if (vboxNetFltWinGetWinIfState(pThis) == kVBoxWinIfState_Disconnected) + { + if (pAttachInfo->fRediscovery) + { + /* rediscovery means adaptor bind is performed while intnet is already using it + * i.e. adaptor was unbound while being used by intnet and now being bound back again */ + Assert( ((VBOXNETFTLINSSTATE)ASMAtomicUoReadU32((uint32_t volatile *)&pThis->enmState)) + == kVBoxNetFltInsState_Connected); + } +#ifndef VBOXNETADP + Status = vboxNetFltWinPtInitWinIf(&pThis->u.s.WinIf, pAttachInfo->pCreateContext->pOurName); +#else + Status = vboxNetFltWinPtInitWinIf(&pThis->u.s.WinIf); +#endif + if (Status == NDIS_STATUS_SUCCESS) + { + vboxNetFltWinSetWinIfState(pThis, kVBoxWinIfState_Connecting); + +#ifndef VBOXNETADP + Status = vboxNetFltWinPtDoBinding(pThis, pAttachInfo->pCreateContext->pOurName, pAttachInfo->pCreateContext->pBindToName); +#else + Status = vboxNetFltWinMpDoInitialization(pThis, pAttachInfo->pCreateContext->hMiniportAdapter, pAttachInfo->pCreateContext->hWrapperConfigurationContext); +#endif + if (Status == NDIS_STATUS_SUCCESS) + { + if (!pAttachInfo->fRediscovery) + vboxNetFltWinDrvReference(); +#ifndef VBOXNETADP + if (pThis->u.s.WinIf.OpenCloseStatus == NDIS_STATUS_SUCCESS) +#endif + { + vboxNetFltWinSetWinIfState(pThis, kVBoxWinIfState_Connected); +#ifndef VBOXNETADP + Assert(vboxNetFltWinGetOpState(&pThis->u.s.WinIf.PtState) == kVBoxNetDevOpState_Initialized); +#endif + /* 4. mark as connected */ + RTSpinlockAcquire(pThis->hSpinlock); + ASMAtomicUoWriteBool(&pThis->fDisconnectedFromHost, false); + RTSpinlockRelease(pThis->hSpinlock); + + pAttachInfo->Status = VINF_SUCCESS; + pAttachInfo->pCreateContext->Status = NDIS_STATUS_SUCCESS; + + RTSemMutexRelease(pThis->u.s.hWinIfMutex); + + vboxNetFltRelease(pThis, false); + + /* 5. Report MAC address, promiscuousness and GSO capabilities. */ + vboxNetFltWinReportStuff(pThis); + + return; + } +#ifndef VBOXNETADP /* unreachable for VBOXNETADP because of the return above */ + AssertBreakpoint(); + + if (!pAttachInfo->fRediscovery) + { + vboxNetFltWinDrvDereference(); + } +# ifndef VBOXNETADP + vboxNetFltWinPtDoUnbinding(pThis, true); +/*# else - unreachable + vboxNetFltWinMpDoDeinitialization(pThis); */ +# endif +#endif + } + AssertBreakpoint(); + vboxNetFltWinPtFiniWinIf(&pThis->u.s.WinIf); + } + AssertBreakpoint(); + vboxNetFltWinSetWinIfState(pThis, kVBoxWinIfState_Disconnected); + Assert(vboxNetFltWinGetOpState(&pThis->u.s.WinIf.MpState) == kVBoxNetDevOpState_Deinitialized); +#ifndef VBOXNETADP + Assert(vboxNetFltWinGetOpState(&pThis->u.s.WinIf.PtState) == kVBoxNetDevOpState_Deinitialized); +#endif + } + AssertBreakpoint(); + + pAttachInfo->Status = VERR_GENERAL_FAILURE; + pAttachInfo->pCreateContext->Status = Status; + RTSemMutexRelease(pThis->u.s.hWinIfMutex); + } + else + { + AssertBreakpoint(); + pAttachInfo->Status = rc; + } + + vboxNetFltRelease(pThis, false); + + return; +} + +/** + * Common code for vboxNetFltOsInitInstance and + * vboxNetFltOsMaybeRediscovered. + * + * @returns IPRT status code. + * @param pThis The instance. + * @param fRediscovery True if vboxNetFltOsMaybeRediscovered is calling, + * false if it's vboxNetFltOsInitInstance. + */ +static int vboxNetFltWinAttachToInterface(PVBOXNETFLTINS pThis, void * pContext, bool fRediscovery) +{ + ATTACH_INFO Info; + Info.pNetFltIf = pThis; + Info.fRediscovery = fRediscovery; + Info.pCreateContext = (PCREATE_INSTANCE_CONTEXT)pContext; + + vboxNetFltWinAttachToInterfaceWorker(&Info); + + return Info.Status; +} +static NTSTATUS vboxNetFltWinPtDevDispatch(IN PDEVICE_OBJECT pDevObj, IN PIRP pIrp) +{ + RT_NOREF1(pDevObj); + PIO_STACK_LOCATION pIrpSl = IoGetCurrentIrpStackLocation(pIrp);; + NTSTATUS Status = STATUS_SUCCESS; + + switch (pIrpSl->MajorFunction) + { + case IRP_MJ_DEVICE_CONTROL: + Status = STATUS_NOT_SUPPORTED; + break; + case IRP_MJ_CREATE: + case IRP_MJ_CLEANUP: + case IRP_MJ_CLOSE: + break; + default: + AssertFailed(); + break; + } + + pIrp->IoStatus.Status = Status; + IoCompleteRequest(pIrp, IO_NO_INCREMENT); + + return Status; +} + +static NDIS_STATUS vboxNetFltWinDevCreate(PVBOXNETFLTGLOBALS_WIN pGlobals) +{ + NDIS_STRING DevName, LinkName; + PDRIVER_DISPATCH aMajorFunctions[IRP_MJ_MAXIMUM_FUNCTION+1]; + NdisInitUnicodeString(&DevName, VBOXNETFLT_NAME_DEVICE); + NdisInitUnicodeString(&LinkName, VBOXNETFLT_NAME_LINK); + + Assert(!pGlobals->hDevice); + Assert(!pGlobals->pDevObj); + NdisZeroMemory(aMajorFunctions, sizeof (aMajorFunctions)); + aMajorFunctions[IRP_MJ_CREATE] = vboxNetFltWinPtDevDispatch; + aMajorFunctions[IRP_MJ_CLEANUP] = vboxNetFltWinPtDevDispatch; + aMajorFunctions[IRP_MJ_CLOSE] = vboxNetFltWinPtDevDispatch; + aMajorFunctions[IRP_MJ_DEVICE_CONTROL] = vboxNetFltWinPtDevDispatch; + + NDIS_STATUS Status = NdisMRegisterDevice(pGlobals->Mp.hNdisWrapper, + &DevName, &LinkName, + aMajorFunctions, + &pGlobals->pDevObj, + &pGlobals->hDevice); + Assert(Status == NDIS_STATUS_SUCCESS); + return Status; +} + +static NDIS_STATUS vboxNetFltWinDevDestroy(PVBOXNETFLTGLOBALS_WIN pGlobals) +{ + Assert(pGlobals->hDevice); + Assert(pGlobals->pDevObj); + NDIS_STATUS Status = NdisMDeregisterDevice(pGlobals->hDevice); + Assert(Status == NDIS_STATUS_SUCCESS); + if (Status == NDIS_STATUS_SUCCESS) + { + pGlobals->hDevice = NULL; + pGlobals->pDevObj = NULL; + } + return Status; +} + +static NDIS_STATUS vboxNetFltWinDevCreateReference(PVBOXNETFLTGLOBALS_WIN pGlobals) +{ + Assert(KeGetCurrentIrql() == PASSIVE_LEVEL); + NDIS_STATUS Status = KeWaitForSingleObject(&pGlobals->SynchEvent, Executive, KernelMode, FALSE, NULL); + Assert(Status == STATUS_SUCCESS); + if (Status == STATUS_SUCCESS) + { + Assert(pGlobals->cDeviceRefs >= 0); + if (++pGlobals->cDeviceRefs == 1) + { + Status = vboxNetFltWinDevCreate(pGlobals); + if (Status == NDIS_STATUS_SUCCESS) + { + ObReferenceObject(pGlobals->pDevObj); + } + } + else + { + Status = NDIS_STATUS_SUCCESS; + } + KeSetEvent(&pGlobals->SynchEvent, 0, FALSE); + } + else + { + /* should never happen actually */ + AssertFailed(); + Status = NDIS_STATUS_FAILURE; + } + return Status; +} + +static NDIS_STATUS vboxNetFltWinDevDereference(PVBOXNETFLTGLOBALS_WIN pGlobals) +{ + Assert(KeGetCurrentIrql() == PASSIVE_LEVEL); + NDIS_STATUS Status = KeWaitForSingleObject(&pGlobals->SynchEvent, Executive, KernelMode, FALSE, NULL); + Assert(Status == STATUS_SUCCESS); + if (Status == STATUS_SUCCESS) + { + Assert(pGlobals->cDeviceRefs > 0); + if (!(--pGlobals->cDeviceRefs)) + { + ObDereferenceObject(pGlobals->pDevObj); + Status = vboxNetFltWinDevDestroy(pGlobals); + } + else + { + Status = NDIS_STATUS_SUCCESS; + } + KeSetEvent(&pGlobals->SynchEvent, 0, FALSE); + } + else + { + /* should never happen actually */ + AssertFailed(); + Status = NDIS_STATUS_FAILURE; + } + return Status; +} + +/* reference the driver module to prevent driver unload */ +DECLHIDDEN(void) vboxNetFltWinDrvReference() +{ + vboxNetFltWinDevCreateReference(&g_VBoxNetFltGlobalsWin); +} + +/* dereference the driver module to prevent driver unload */ +DECLHIDDEN(void) vboxNetFltWinDrvDereference() +{ + vboxNetFltWinDevDereference(&g_VBoxNetFltGlobalsWin); +} + +/* + * + * The OS specific interface definition + * + */ + + +bool vboxNetFltOsMaybeRediscovered(PVBOXNETFLTINS pThis) +{ + /* AttachToInterface true if disconnected */ + return !ASMAtomicUoReadBool(&pThis->fDisconnectedFromHost); +} + +int vboxNetFltPortOsXmit(PVBOXNETFLTINS pThis, void *pvIfData, PINTNETSG pSG, uint32_t fDst) +{ + RT_NOREF1(pvIfData); + int rc = VINF_SUCCESS; + uint32_t cRefs = 0; +#ifndef VBOXNETADP + if (fDst & INTNETTRUNKDIR_WIRE) + cRefs++; + if (fDst & INTNETTRUNKDIR_HOST) + cRefs++; +#else + if ((fDst & INTNETTRUNKDIR_WIRE) || (fDst & INTNETTRUNKDIR_HOST)) + cRefs = 1; +#endif + + AssertReturn(cRefs, VINF_SUCCESS); + + if (!vboxNetFltWinIncReferenceWinIf(pThis, cRefs)) + { + return VERR_GENERAL_FAILURE; + } +#ifndef VBOXNETADP + if (fDst & INTNETTRUNKDIR_WIRE) + { + PNDIS_PACKET pPacket; + + pPacket = vboxNetFltWinNdisPacketFromSG(pThis, pSG, NULL /*pBufToFree*/, + true /*fToWire*/, true /*fCopyMemory*/); + + if (pPacket) + { + NDIS_STATUS fStatus; + +#ifndef VBOX_LOOPBACK_USEFLAGS + /* force "don't loopback" flags to prevent loopback branch invocation in any case + * to avoid ndis misbehave */ + NdisGetPacketFlags(pPacket) |= g_VBoxNetFltGlobalsWin.fPacketDontLoopBack; +#else + /* this is done by default in vboxNetFltWinNdisPacketFromSG */ +#endif + +#if defined(DEBUG_NETFLT_PACKETS) || !defined(VBOX_LOOPBACK_USEFLAGS) + vboxNetFltWinLbPutSendPacket(pThis, pPacket, true /* bFromIntNet */); +#endif + NdisSend(&fStatus, pThis->u.s.WinIf.hBinding, pPacket); + if (fStatus != NDIS_STATUS_PENDING) + { +#if defined(DEBUG_NETFLT_PACKETS) || !defined(VBOX_LOOPBACK_USEFLAGS) + /* the status is NOT pending, complete the packet */ + bool fTmp = vboxNetFltWinLbRemoveSendPacket(pThis, pPacket); + Assert(fTmp); NOREF(fTmp); +#endif + if (!NT_SUCCESS(fStatus)) + rc = VERR_GENERAL_FAILURE; /** @todo convert status to VERR_xxx */ + + vboxNetFltWinFreeSGNdisPacket(pPacket, true); + } + else + { + /* pending, dereference on packet complete */ + cRefs--; + } + } + else + { + AssertFailed(); + rc = VERR_NO_MEMORY; + } + } +#endif + +#ifndef VBOXNETADP + if (fDst & INTNETTRUNKDIR_HOST) +#else + if (cRefs) +#endif + { + PNDIS_PACKET pPacket = vboxNetFltWinNdisPacketFromSG(pThis, pSG, NULL /*pBufToFree*/, + false /*fToWire*/, true /*fCopyMemory*/); + if (pPacket) + { + NdisMIndicateReceivePacket(pThis->u.s.WinIf.hMiniport, &pPacket, 1); + cRefs--; +#ifdef VBOXNETADP + STATISTIC_INCREASE(pThis->u.s.WinIf.cRxSuccess); +#endif + } + else + { + AssertFailed(); +#ifdef VBOXNETADP + STATISTIC_INCREASE(pThis->u.s.WinIf.cRxError); +#endif + rc = VERR_NO_MEMORY; + } + } + + Assert(cRefs <= 2); + + if (cRefs) + { + vboxNetFltWinDecReferenceWinIf(pThis, cRefs); + } + + return rc; +} + +void vboxNetFltPortOsSetActive(PVBOXNETFLTINS pThis, bool fActive) +{ +#ifndef VBOXNETADP + NDIS_STATUS Status; +#endif + /* we first wait for all pending ops to complete + * this might include all packets queued for processing */ + for (;;) + { + if (fActive) + { + if (!pThis->u.s.cModePassThruRefs) + { + break; + } + } + else + { + if (!pThis->u.s.cModeNetFltRefs) + { + break; + } + } + vboxNetFltWinSleep(2); + } + + if (!vboxNetFltWinReferenceWinIf(pThis)) + return; +#ifndef VBOXNETADP + + if (fActive) + { +#ifdef DEBUG_misha + NDIS_PHYSICAL_MEDIUM PhMedium; + bool bPromiscSupported; + + Status = vboxNetFltWinQueryPhysicalMedium(pThis, &PhMedium); + if (Status != NDIS_STATUS_SUCCESS) + { + + LogRel(("vboxNetFltWinQueryPhysicalMedium failed, Status (0x%x), setting medium to NdisPhysicalMediumUnspecified\n", Status)); + Assert(Status == NDIS_STATUS_NOT_SUPPORTED); + if (Status != NDIS_STATUS_NOT_SUPPORTED) + { + LogRel(("vboxNetFltWinQueryPhysicalMedium failed, Status (0x%x), setting medium to NdisPhysicalMediumUnspecified\n", Status)); + } + PhMedium = NdisPhysicalMediumUnspecified; + } + else + { + LogRel(("(SUCCESS) vboxNetFltWinQueryPhysicalMedium SUCCESS\n")); + } + + bPromiscSupported = (!(PhMedium == NdisPhysicalMediumWirelessWan + || PhMedium == NdisPhysicalMediumWirelessLan + || PhMedium == NdisPhysicalMediumNative802_11 + || PhMedium == NdisPhysicalMediumBluetooth + /*|| PhMedium == NdisPhysicalMediumWiMax */ + )); + + Assert(bPromiscSupported == VBOXNETFLT_PROMISCUOUS_SUPPORTED(pThis)); +#endif + } + + if (VBOXNETFLT_PROMISCUOUS_SUPPORTED(pThis)) + { + Status = vboxNetFltWinSetPromiscuous(pThis, fActive); + if (Status != NDIS_STATUS_SUCCESS) + { + LogRel(("vboxNetFltWinSetPromiscuous failed, Status (0x%x), fActive (%d)\n", Status, fActive)); + AssertFailed(); + } + } +#else +# ifdef VBOXNETADP_REPORT_DISCONNECTED + if (fActive) + { + NdisMIndicateStatus(pThis->u.s.WinIf.hMiniport, + NDIS_STATUS_MEDIA_CONNECT, + (PVOID)NULL, + 0); + } + else + { + NdisMIndicateStatus(pThis->u.s.WinIf.hMiniport, + NDIS_STATUS_MEDIA_DISCONNECT, + (PVOID)NULL, + 0); + } +#else + if (fActive) + { + /* indicate status change to make the ip settings be re-picked for dhcp */ + NdisMIndicateStatus(pThis->u.s.WinIf.hMiniport, + NDIS_STATUS_MEDIA_DISCONNECT, + (PVOID)NULL, + 0); + + NdisMIndicateStatus(pThis->u.s.WinIf.hMiniport, + NDIS_STATUS_MEDIA_CONNECT, + (PVOID)NULL, + 0); + } +# endif +#endif + vboxNetFltWinDereferenceWinIf(pThis); + + return; +} + +#ifndef VBOXNETADP + +DECLINLINE(bool) vboxNetFltWinIsAddrLinkLocal4(PCRTNETADDRIPV4 pAddr) +{ + return (pAddr->s.Lo == 0xfea9); /* 169.254 */ +} + +DECLINLINE(bool) vboxNetFltWinIsAddrLinkLocal6(PCRTNETADDRIPV6 pAddr) +{ + return ((pAddr->au8[0] == 0xfe) && ((pAddr->au8[1] & 0xc0) == 0x80)); +} + +void vboxNetFltWinNotifyHostAddress(PTA_ADDRESS pAddress, bool fAdded) +{ + void *pvAddr = NULL; + INTNETADDRTYPE enmAddrType = kIntNetAddrType_Invalid; + + LogFlow(("==>vboxNetFltWinNotifyHostAddress: AddrType=%d %s\n", + pAddress->AddressType, fAdded ? "added" : "deleted")); + if (pAddress->AddressType == TDI_ADDRESS_TYPE_IP) + { + PTDI_ADDRESS_IP pTdiAddrIp = (PTDI_ADDRESS_IP)pAddress->Address; + /* + * Note that we do not get loopback addresses here. If we did we should + * have checked and ignored them too. + */ + if (!vboxNetFltWinIsAddrLinkLocal4((PCRTNETADDRIPV4)(&pTdiAddrIp->in_addr))) + { + pvAddr = &pTdiAddrIp->in_addr; + enmAddrType = kIntNetAddrType_IPv4; + } + else + Log2(("vboxNetFltWinNotifyHostAddress: ignoring link-local address %RTnaipv4\n", + pTdiAddrIp->in_addr)); + } + else if (pAddress->AddressType == TDI_ADDRESS_TYPE_IP6) + { + PTDI_ADDRESS_IP6 pTdiAddrIp6 = (PTDI_ADDRESS_IP6)pAddress->Address; + if (!vboxNetFltWinIsAddrLinkLocal6((PCRTNETADDRIPV6)(pTdiAddrIp6->sin6_addr))) + { + pvAddr = pTdiAddrIp6->sin6_addr; + enmAddrType = kIntNetAddrType_IPv6; + } + else + Log2(("vboxNetFltWinNotifyHostAddress: ignoring link-local address %RTnaipv6\n", + pTdiAddrIp6->sin6_addr)); + } + else + { + Log2(("vboxNetFltWinNotifyHostAddress: ignoring irrelevant address type %d\n", + pAddress->AddressType)); + LogFlow(("<==vboxNetFltWinNotifyHostAddress\n")); + return; + } + if (pvAddr) + { + NdisAcquireSpinLock(&g_VBoxNetFltGlobalsWin.lockFilters); + /* At this point the list must contain at least one element. */ + PVBOXNETFLTINS pInstance = NULL; + PVBOXNETFLTWIN pFilter; + RTListForEach(&g_VBoxNetFltGlobalsWin.listFilters, pFilter, VBOXNETFLTWIN, node) + { + pInstance = RT_FROM_MEMBER(pFilter, VBOXNETFLTINS, u.s.WinIf); + if (vboxNetFltWinReferenceWinIf(pInstance)) + { + if (pInstance->pSwitchPort && pInstance->pSwitchPort->pfnNotifyHostAddress) + break; + vboxNetFltWinDereferenceWinIf(pInstance); + } + else + Log2(("vboxNetFltWinNotifyHostAddress: failed to retain filter instance %p\n", pInstance)); + pInstance = NULL; + } + NdisReleaseSpinLock(&g_VBoxNetFltGlobalsWin.lockFilters); + if (pInstance) + { + if (enmAddrType == kIntNetAddrType_IPv4) + Log2(("vboxNetFltWin%sAddressHandler: %RTnaipv4\n", + fAdded ? "Add" : "Del", *(PCRTNETADDRIPV4)pvAddr)); + else + Log2(("vboxNetFltWin%sAddressHandler: %RTnaipv6\n", + fAdded ? "Add" : "Del", pvAddr)); + pInstance->pSwitchPort->pfnNotifyHostAddress(pInstance->pSwitchPort, fAdded, + enmAddrType, pvAddr); + vboxNetFltWinDereferenceWinIf(pInstance); + } + else + Log2(("vboxNetFltWinNotifyHostAddress: no filters require notification\n")); + } + LogFlow(("<==vboxNetFltWinNotifyHostAddress\n")); +} + +void vboxNetFltWinAddAddressHandler(PTA_ADDRESS Address, + PUNICODE_STRING DeviceName, + PTDI_PNP_CONTEXT Context) +{ + RT_NOREF2(DeviceName, Context); + vboxNetFltWinNotifyHostAddress(Address, true); +} + +void vboxNetFltWinDelAddressHandler(PTA_ADDRESS Address, + PUNICODE_STRING DeviceName, + PTDI_PNP_CONTEXT Context) +{ + RT_NOREF2(DeviceName, Context); + vboxNetFltWinNotifyHostAddress(Address, false); +} + +void vboxNetFltWinRegisterIpAddrNotifier(PVBOXNETFLTINS pThis) +{ + LogFlow(("==>vboxNetFltWinRegisterIpAddrNotifier: instance=%p pThis->pSwitchPort=%p pThis->pSwitchPort->pfnNotifyHostAddress=%p\n", + pThis, pThis->pSwitchPort, pThis->pSwitchPort ? pThis->pSwitchPort->pfnNotifyHostAddress : NULL)); + if (pThis->pSwitchPort && pThis->pSwitchPort->pfnNotifyHostAddress) + { + NdisAcquireSpinLock(&g_VBoxNetFltGlobalsWin.lockFilters); + bool fRegisterHandlers = RTListIsEmpty(&g_VBoxNetFltGlobalsWin.listFilters); + RTListPrepend(&g_VBoxNetFltGlobalsWin.listFilters, &pThis->u.s.WinIf.node); + NdisReleaseSpinLock(&g_VBoxNetFltGlobalsWin.lockFilters); + + if (fRegisterHandlers) + { + TDI_CLIENT_INTERFACE_INFO Info; + UNICODE_STRING ClientName = RTL_CONSTANT_STRING(L"VBoxNetFlt"); + memset(&Info, 0, sizeof(Info)); + Info.MajorTdiVersion = 2; + Info.MinorTdiVersion = 0; + Info.ClientName = &ClientName; + Info.AddAddressHandlerV2 = vboxNetFltWinAddAddressHandler; + Info.DelAddressHandlerV2 = vboxNetFltWinDelAddressHandler; + Assert(!g_VBoxNetFltGlobalsWin.hNotifier); + NTSTATUS Status = TdiRegisterPnPHandlers(&Info, sizeof(Info), &g_VBoxNetFltGlobalsWin.hNotifier); + Log2(("vboxNetFltWinRegisterIpAddrNotifier: TdiRegisterPnPHandlers returned %d\n", Status)); NOREF(Status); + } + else + Log2(("vboxNetFltWinRegisterIpAddrNotifier: already registed\n")); + } + else + Log2(("vboxNetFltWinRegisterIpAddrNotifier: this instance does not require notifications, ignoring...\n")); + LogFlow(("<==vboxNetFltWinRegisterIpAddrNotifier: notifier=%p\n", g_VBoxNetFltGlobalsWin.hNotifier)); +} + +void vboxNetFltWinUnregisterIpAddrNotifier(PVBOXNETFLTINS pThis) +{ + LogFlow(("==>vboxNetFltWinUnregisterIpAddrNotifier: notifier=%p\n", g_VBoxNetFltGlobalsWin.hNotifier)); + if (pThis->pSwitchPort && pThis->pSwitchPort->pfnNotifyHostAddress) + { + NdisAcquireSpinLock(&g_VBoxNetFltGlobalsWin.lockFilters); + /* At this point the list must contain at least one element. */ + Assert(!RTListIsEmpty(&g_VBoxNetFltGlobalsWin.listFilters)); + RTListNodeRemove(&pThis->u.s.WinIf.node); + HANDLE hNotifier = NULL; + if (RTListIsEmpty(&g_VBoxNetFltGlobalsWin.listFilters)) + { + /* + * The list has become empty, so we need to deregister handlers. We + * grab hNotifier and reset it while still holding the lock. This + * guaranties that we won't interfere with setting it in + * vboxNetFltWinRegisterIpAddrNotifier(). It is inconceivable that + * vboxNetFltWinUnregisterIpAddrNotifier() will be called for the + * same filter instance while it is still being processed by + * vboxNetFltWinRegisterIpAddrNotifier(). This would require trunk + * destruction in the middle of its creation. It is possible that + * vboxNetFltWinUnregisterIpAddrNotifier() is called for another + * filter instance, but in such case we won't even get here as the + * list won't be empty. + */ + hNotifier = g_VBoxNetFltGlobalsWin.hNotifier; + g_VBoxNetFltGlobalsWin.hNotifier = NULL; + } + NdisReleaseSpinLock(&g_VBoxNetFltGlobalsWin.lockFilters); + if (hNotifier) + { + NTSTATUS Status = TdiDeregisterPnPHandlers(hNotifier); + Log2(("vboxNetFltWinUnregisterIpAddrNotifier: TdiDeregisterPnPHandlers(%p) returned %d\n", + hNotifier, Status)); NOREF(Status); + } + else + Log2(("vboxNetFltWinUnregisterIpAddrNotifier: filters remain, do not deregister handlers yet\n")); + } + else + Log2(("vboxNetFltWinUnregisterIpAddrNotifier: this instance did not require notifications, ignoring...\n")); + LogFlow(("<==vboxNetFltWinUnregisterIpAddrNotifier\n")); +} +#else /* VBOXNETADP */ +#define vboxNetFltWinRegisterIpAddrNotifier(x) +#define vboxNetFltWinUnregisterIpAddrNotifier(x) +#endif /* VBOXNETADP */ + +int vboxNetFltOsDisconnectIt(PVBOXNETFLTINS pThis) +{ + NDIS_STATUS Status = vboxNetFltWinDisconnectIt(pThis); + Log2(("vboxNetFltOsDisconnectIt: pThis=%p pThis->pSwitchPort=%p pThis->pSwitchPort->pfnNotifyHostAddress=%p\n", + pThis, pThis->pSwitchPort, pThis->pSwitchPort ? pThis->pSwitchPort->pfnNotifyHostAddress : NULL)); + vboxNetFltWinUnregisterIpAddrNotifier(pThis); + return Status == NDIS_STATUS_SUCCESS ? VINF_SUCCESS : VERR_GENERAL_FAILURE; +} + +static void vboxNetFltWinConnectItWorker(PVOID pvContext) +{ + PWORKER_INFO pInfo = (PWORKER_INFO)pvContext; +#if !defined(VBOXNETADP) || !defined(VBOXNETFLT_NO_PACKET_QUEUE) + NDIS_STATUS Status; +#endif + PVBOXNETFLTINS pInstance = pInfo->pNetFltIf; + + Assert(KeGetCurrentIrql() == PASSIVE_LEVEL); + + /* this is not a rediscovery, initialize Mac cache */ + if (vboxNetFltWinReferenceWinIf(pInstance)) + { +#ifndef VBOXNETADP + Status = vboxNetFltWinGetMacAddress(pInstance, &pInstance->u.s.MacAddr); + if (Status == NDIS_STATUS_SUCCESS) +#endif + { +#ifdef VBOXNETFLT_NO_PACKET_QUEUE + pInfo->Status = VINF_SUCCESS; +#else + Status = vboxNetFltWinQuInitPacketQueue(pInstance); + if (Status == NDIS_STATUS_SUCCESS) + { + pInfo->Status = VINF_SUCCESS; + } + else + { + pInfo->Status = VERR_GENERAL_FAILURE; + } +#endif + } +#ifndef VBOXNETADP + else + { + pInfo->Status = VERR_INTNET_FLT_IF_FAILED; + } +#endif + + vboxNetFltWinDereferenceWinIf(pInstance); + } + else + { + pInfo->Status = VERR_INTNET_FLT_IF_NOT_FOUND; + } +} + +static int vboxNetFltWinConnectIt(PVBOXNETFLTINS pThis) +{ + WORKER_INFO Info; + Info.pNetFltIf = pThis; + + vboxNetFltWinJobSynchExecAtPassive(vboxNetFltWinConnectItWorker, &Info); + + if (RT_SUCCESS(Info.Status)) + vboxNetFltWinReportStuff(pThis); + + return Info.Status; +} + +int vboxNetFltOsConnectIt(PVBOXNETFLTINS pThis) +{ + Log2(("vboxNetFltOsConnectIt: pThis=%p pThis->pSwitchPort=%p pThis->pSwitchPort->pfnNotifyHostAddress=%p\n", + pThis, pThis->pSwitchPort, pThis->pSwitchPort ? pThis->pSwitchPort->pfnNotifyHostAddress : NULL)); + vboxNetFltWinRegisterIpAddrNotifier(pThis); + return vboxNetFltWinConnectIt(pThis); +} + +void vboxNetFltOsDeleteInstance(PVBOXNETFLTINS pThis) +{ + vboxNetFltWinDeleteInstance(pThis); +} + +int vboxNetFltOsInitInstance(PVBOXNETFLTINS pThis, void *pvContext) +{ + int rc = RTSemMutexCreate(&pThis->u.s.hWinIfMutex); + if (RT_SUCCESS(rc)) + { + rc = vboxNetFltWinAttachToInterface(pThis, pvContext, false /*fRediscovery*/ ); + if (RT_SUCCESS(rc)) + { + return rc; + } + RTSemMutexDestroy(pThis->u.s.hWinIfMutex); + } + return rc; +} + +int vboxNetFltOsPreInitInstance(PVBOXNETFLTINS pThis) +{ + pThis->u.s.cModeNetFltRefs = 0; + pThis->u.s.cModePassThruRefs = 0; + vboxNetFltWinSetWinIfState(pThis, kVBoxWinIfState_Disconnected); + vboxNetFltWinSetOpState(&pThis->u.s.WinIf.MpState, kVBoxNetDevOpState_Deinitialized); +#ifndef VBOXNETADP + vboxNetFltWinSetOpState(&pThis->u.s.WinIf.PtState, kVBoxNetDevOpState_Deinitialized); +#endif + return VINF_SUCCESS; +} + +void vboxNetFltPortOsNotifyMacAddress(PVBOXNETFLTINS pThis, void *pvIfData, PCRTMAC pMac) +{ + RT_NOREF3(pThis, pvIfData, pMac); +} + +int vboxNetFltPortOsConnectInterface(PVBOXNETFLTINS pThis, void *pvIf, void **ppvIfData) +{ + /* Nothing to do */ + RT_NOREF3(pThis, pvIf, ppvIfData); + return VINF_SUCCESS; +} + +int vboxNetFltPortOsDisconnectInterface(PVBOXNETFLTINS pThis, void *pvIfData) +{ + /* Nothing to do */ + RT_NOREF2(pThis, pvIfData); + return VINF_SUCCESS; +} diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltRt-win.h b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltRt-win.h new file mode 100644 index 00000000..5f08052a --- /dev/null +++ b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetFltRt-win.h @@ -0,0 +1,962 @@ +/* $Id: VBoxNetFltRt-win.h $ */ +/** @file + * VBoxNetFltRt-win.h - Bridged Networking Driver, Windows Specific Code. + * NetFlt Runtime API + */ +/* + * Copyright (C) 2011-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * 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. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE 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. + */ + +#ifndef VBOX_INCLUDED_SRC_VBoxNetFlt_win_drv_VBoxNetFltRt_win_h +#define VBOX_INCLUDED_SRC_VBoxNetFlt_win_drv_VBoxNetFltRt_win_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif +DECLHIDDEN(VOID) vboxNetFltWinUnload(IN PDRIVER_OBJECT DriverObject); + +#ifndef VBOXNETADP +# if !defined(VBOX_LOOPBACK_USEFLAGS) || defined(DEBUG_NETFLT_PACKETS) +DECLHIDDEN(bool) vboxNetFltWinMatchPackets(PNDIS_PACKET pPacket1, PNDIS_PACKET pPacket2, const INT cbMatch); +DECLHIDDEN(bool) vboxNetFltWinMatchPacketAndSG(PNDIS_PACKET pPacket, PINTNETSG pSG, const INT cbMatch); +# endif +#endif + +/************************* + * packet queue API * + *************************/ + + +#define LIST_ENTRY_2_PACKET_INFO(pListEntry) \ + ( (PVBOXNETFLT_PACKET_INFO)((uint8_t *)(pListEntry) - RT_UOFFSETOF(VBOXNETFLT_PACKET_INFO, ListEntry)) ) + +#if !defined(VBOX_LOOPBACK_USEFLAGS) || defined(DEBUG_NETFLT_PACKETS) + +#define VBOX_SLE_2_PKTRSVD_PT(_pEntry) \ + ( (PVBOXNETFLT_PKTRSVD_PT)((uint8_t *)(_pEntry) - RT_UOFFSETOF(VBOXNETFLT_PKTRSVD_PT, ListEntry)) ) + +#define VBOX_SLE_2_SENDPACKET(_pEntry) \ + ( (PNDIS_PACKET)((uint8_t *)(VBOX_SLE_2_PKTRSVD_PT(_pEntry)) - RT_UOFFSETOF(NDIS_PACKET, ProtocolReserved)) ) + +#endif +/** + * enqueus the packet info to the tail of the queue + */ +DECLINLINE(void) vboxNetFltWinQuEnqueueTail(PVBOXNETFLT_PACKET_QUEUE pQueue, PVBOXNETFLT_PACKET_INFO pPacketInfo) +{ + InsertTailList(pQueue, &pPacketInfo->ListEntry); +} + +DECLINLINE(void) vboxNetFltWinQuEnqueueHead(PVBOXNETFLT_PACKET_QUEUE pQueue, PVBOXNETFLT_PACKET_INFO pPacketInfo) +{ + Assert(pPacketInfo->pPool); + InsertHeadList(pQueue, &pPacketInfo->ListEntry); +} + +/** + * enqueus the packet info to the tail of the queue + */ +DECLINLINE(void) vboxNetFltWinQuInterlockedEnqueueTail(PVBOXNETFLT_INTERLOCKED_PACKET_QUEUE pQueue, PVBOXNETFLT_PACKET_INFO pPacketInfo) +{ + Assert(pPacketInfo->pPool); + NdisAcquireSpinLock(&pQueue->Lock); + vboxNetFltWinQuEnqueueTail(&pQueue->Queue, pPacketInfo); + NdisReleaseSpinLock(&pQueue->Lock); +} + +DECLINLINE(void) vboxNetFltWinQuInterlockedEnqueueHead(PVBOXNETFLT_INTERLOCKED_PACKET_QUEUE pQueue, PVBOXNETFLT_PACKET_INFO pPacketInfo) +{ + NdisAcquireSpinLock(&pQueue->Lock); + vboxNetFltWinQuEnqueueHead(&pQueue->Queue, pPacketInfo); + NdisReleaseSpinLock(&pQueue->Lock); +} + +/** + * dequeus the packet info from the head of the queue + */ +DECLINLINE(PVBOXNETFLT_PACKET_INFO) vboxNetFltWinQuDequeueHead(PVBOXNETFLT_PACKET_QUEUE pQueue) +{ + PLIST_ENTRY pListEntry = RemoveHeadList(pQueue); + if (pListEntry != pQueue) + { + PVBOXNETFLT_PACKET_INFO pInfo = LIST_ENTRY_2_PACKET_INFO(pListEntry); + Assert(pInfo->pPool); + return pInfo; + } + return NULL; +} + +DECLINLINE(PVBOXNETFLT_PACKET_INFO) vboxNetFltWinQuDequeueTail(PVBOXNETFLT_PACKET_QUEUE pQueue) +{ + PLIST_ENTRY pListEntry = RemoveTailList(pQueue); + if (pListEntry != pQueue) + { + PVBOXNETFLT_PACKET_INFO pInfo = LIST_ENTRY_2_PACKET_INFO(pListEntry); + Assert(pInfo->pPool); + return pInfo; + } + return NULL; +} + +DECLINLINE(PVBOXNETFLT_PACKET_INFO) vboxNetFltWinQuInterlockedDequeueHead(PVBOXNETFLT_INTERLOCKED_PACKET_QUEUE pInterlockedQueue) +{ + PVBOXNETFLT_PACKET_INFO pInfo; + NdisAcquireSpinLock(&pInterlockedQueue->Lock); + pInfo = vboxNetFltWinQuDequeueHead(&pInterlockedQueue->Queue); + NdisReleaseSpinLock(&pInterlockedQueue->Lock); + return pInfo; +} + +DECLINLINE(PVBOXNETFLT_PACKET_INFO) vboxNetFltWinQuInterlockedDequeueTail(PVBOXNETFLT_INTERLOCKED_PACKET_QUEUE pInterlockedQueue) +{ + PVBOXNETFLT_PACKET_INFO pInfo; + NdisAcquireSpinLock(&pInterlockedQueue->Lock); + pInfo = vboxNetFltWinQuDequeueTail(&pInterlockedQueue->Queue); + NdisReleaseSpinLock(&pInterlockedQueue->Lock); + return pInfo; +} + +DECLINLINE(void) vboxNetFltWinQuDequeue(PVBOXNETFLT_PACKET_INFO pInfo) +{ + RemoveEntryList(&pInfo->ListEntry); +} + +DECLINLINE(void) vboxNetFltWinQuInterlockedDequeue(PVBOXNETFLT_INTERLOCKED_PACKET_QUEUE pInterlockedQueue, PVBOXNETFLT_PACKET_INFO pInfo) +{ + NdisAcquireSpinLock(&pInterlockedQueue->Lock); + vboxNetFltWinQuDequeue(pInfo); + NdisReleaseSpinLock(&pInterlockedQueue->Lock); +} + +/** + * allocates the packet info from the pool + */ +DECLINLINE(PVBOXNETFLT_PACKET_INFO) vboxNetFltWinPpAllocPacketInfo(PVBOXNETFLT_PACKET_INFO_POOL pPool) +{ + return vboxNetFltWinQuInterlockedDequeueHead(&pPool->Queue); +} + +/** + * returns the packet info to the pool + */ +DECLINLINE(void) vboxNetFltWinPpFreePacketInfo(PVBOXNETFLT_PACKET_INFO pInfo) +{ + PVBOXNETFLT_PACKET_INFO_POOL pPool = pInfo->pPool; + vboxNetFltWinQuInterlockedEnqueueHead(&pPool->Queue, pInfo); +} + +/** initializes the packet queue */ +#define INIT_PACKET_QUEUE(_pQueue) InitializeListHead((_pQueue)) + +/** initializes the packet queue */ +#define INIT_INTERLOCKED_PACKET_QUEUE(_pQueue) \ + { \ + INIT_PACKET_QUEUE(&(_pQueue)->Queue); \ + NdisAllocateSpinLock(&(_pQueue)->Lock); \ + } + +/** delete the packet queue */ +#define FINI_INTERLOCKED_PACKET_QUEUE(_pQueue) NdisFreeSpinLock(&(_pQueue)->Lock) + +/** returns the packet the packet info contains */ +#define GET_PACKET_FROM_INFO(_pPacketInfo) (ASMAtomicUoReadPtr((void * volatile *)&(_pPacketInfo)->pPacket)) + +/** assignes the packet to the packet info */ +#define SET_PACKET_TO_INFO(_pPacketInfo, _pPacket) (ASMAtomicUoWritePtr(&(_pPacketInfo)->pPacket, (_pPacket))) + +/** returns the flags the packet info contains */ +#define GET_FLAGS_FROM_INFO(_pPacketInfo) (ASMAtomicUoReadU32((volatile uint32_t *)&(_pPacketInfo)->fFlags)) + +/** sets flags to the packet info */ +#define SET_FLAGS_TO_INFO(_pPacketInfo, _fFlags) (ASMAtomicUoWriteU32((volatile uint32_t *)&(_pPacketInfo)->fFlags, (_fFlags))) + +#ifdef VBOXNETFLT_NO_PACKET_QUEUE +DECLHIDDEN(bool) vboxNetFltWinPostIntnet(PVBOXNETFLTINS pInstance, PVOID pvPacket, const UINT fFlags); +#else +DECLHIDDEN(NDIS_STATUS) vboxNetFltWinQuEnqueuePacket(PVBOXNETFLTINS pInstance, PVOID pPacket, const UINT fPacketFlags); +DECLHIDDEN(void) vboxNetFltWinQuFiniPacketQueue(PVBOXNETFLTINS pInstance); +DECLHIDDEN(NTSTATUS) vboxNetFltWinQuInitPacketQueue(PVBOXNETFLTINS pInstance); +#endif /* #ifndef VBOXNETFLT_NO_PACKET_QUEUE */ + + +#ifndef VBOXNETADP +/** + * searches the list entry in a single-linked list + */ +DECLINLINE(bool) vboxNetFltWinSearchListEntry(PVBOXNETFLT_SINGLE_LIST pList, PSINGLE_LIST_ENTRY pEntry2Search, bool bRemove) +{ + PSINGLE_LIST_ENTRY pHead = &pList->Head; + PSINGLE_LIST_ENTRY pCur; + PSINGLE_LIST_ENTRY pPrev; + for (pCur = pHead->Next, pPrev = pHead; pCur; pPrev = pCur, pCur = pCur->Next) + { + if (pEntry2Search == pCur) + { + if (bRemove) + { + pPrev->Next = pCur->Next; + if (pCur == pList->pTail) + { + pList->pTail = pPrev; + } + } + return true; + } + } + return false; +} + +#if !defined(VBOX_LOOPBACK_USEFLAGS) || defined(DEBUG_NETFLT_PACKETS) + +DECLINLINE(PNDIS_PACKET) vboxNetFltWinSearchPacket(PVBOXNETFLT_SINGLE_LIST pList, PNDIS_PACKET pPacket2Search, int cbMatch, bool bRemove) +{ + PSINGLE_LIST_ENTRY pHead = &pList->Head; + PSINGLE_LIST_ENTRY pCur; + PSINGLE_LIST_ENTRY pPrev; + PNDIS_PACKET pCurPacket; + for (pCur = pHead->Next, pPrev = pHead; pCur; pPrev = pCur, pCur = pCur->Next) + { + pCurPacket = VBOX_SLE_2_SENDPACKET(pCur); + if (pCurPacket == pPacket2Search || vboxNetFltWinMatchPackets(pPacket2Search, pCurPacket, cbMatch)) + { + if (bRemove) + { + pPrev->Next = pCur->Next; + if (pCur == pList->pTail) + { + pList->pTail = pPrev; + } + } + return pCurPacket; + } + } + return NULL; +} + +DECLINLINE(PNDIS_PACKET) vboxNetFltWinSearchPacketBySG(PVBOXNETFLT_SINGLE_LIST pList, PINTNETSG pSG, int cbMatch, bool bRemove) +{ + PSINGLE_LIST_ENTRY pHead = &pList->Head; + PSINGLE_LIST_ENTRY pCur; + PSINGLE_LIST_ENTRY pPrev; + PNDIS_PACKET pCurPacket; + for (pCur = pHead->Next, pPrev = pHead; pCur; pPrev = pCur, pCur = pCur->Next) + { + pCurPacket = VBOX_SLE_2_SENDPACKET(pCur); + if (vboxNetFltWinMatchPacketAndSG(pCurPacket, pSG, cbMatch)) + { + if (bRemove) + { + pPrev->Next = pCur->Next; + if (pCur == pList->pTail) + { + pList->pTail = pPrev; + } + } + return pCurPacket; + } + } + return NULL; +} + +#endif /* #if !defined(VBOX_LOOPBACK_USEFLAGS) || defined(DEBUG_NETFLT_PACKETS) */ + +DECLINLINE(bool) vboxNetFltWinSListIsEmpty(PVBOXNETFLT_SINGLE_LIST pList) +{ + return !pList->Head.Next; +} + +DECLINLINE(void) vboxNetFltWinPutTail(PVBOXNETFLT_SINGLE_LIST pList, PSINGLE_LIST_ENTRY pEntry) +{ + pList->pTail->Next = pEntry; + pList->pTail = pEntry; + pEntry->Next = NULL; +} + +DECLINLINE(void) vboxNetFltWinPutHead(PVBOXNETFLT_SINGLE_LIST pList, PSINGLE_LIST_ENTRY pEntry) +{ + pEntry->Next = pList->Head.Next; + pList->Head.Next = pEntry; + if (!pEntry->Next) + pList->pTail = pEntry; +} + +DECLINLINE(PSINGLE_LIST_ENTRY) vboxNetFltWinGetHead(PVBOXNETFLT_SINGLE_LIST pList) +{ + PSINGLE_LIST_ENTRY pEntry = pList->Head.Next; + if (pEntry && pEntry == pList->pTail) + { + pList->Head.Next = NULL; + pList->pTail = &pList->Head; + } + return pEntry; +} + +DECLINLINE(bool) vboxNetFltWinInterlockedSearchListEntry(PVBOXNETFLT_INTERLOCKED_SINGLE_LIST pList, PSINGLE_LIST_ENTRY pEntry2Search, bool bRemove) +{ + bool bFound; + NdisAcquireSpinLock(&pList->Lock); + bFound = vboxNetFltWinSearchListEntry(&pList->List, pEntry2Search, bRemove); + NdisReleaseSpinLock(&pList->Lock); + return bFound; +} + +#if !defined(VBOX_LOOPBACK_USEFLAGS) || defined(DEBUG_NETFLT_PACKETS) + +DECLINLINE(PNDIS_PACKET) vboxNetFltWinInterlockedSearchPacket(PVBOXNETFLT_INTERLOCKED_SINGLE_LIST pList, PNDIS_PACKET pPacket2Search, int cbMatch, bool bRemove) +{ + PNDIS_PACKET pFound; + NdisAcquireSpinLock(&pList->Lock); + pFound = vboxNetFltWinSearchPacket(&pList->List, pPacket2Search, cbMatch, bRemove); + NdisReleaseSpinLock(&pList->Lock); + return pFound; +} + +DECLINLINE(PNDIS_PACKET) vboxNetFltWinInterlockedSearchPacketBySG(PVBOXNETFLT_INTERLOCKED_SINGLE_LIST pList, PINTNETSG pSG, int cbMatch, bool bRemove) +{ + PNDIS_PACKET pFound; + NdisAcquireSpinLock(&pList->Lock); + pFound = vboxNetFltWinSearchPacketBySG(&pList->List, pSG, cbMatch, bRemove); + NdisReleaseSpinLock(&pList->Lock); + return pFound; +} +#endif /* #if !defined(VBOX_LOOPBACK_USEFLAGS) || defined(DEBUG_NETFLT_PACKETS) */ + +DECLINLINE(void) vboxNetFltWinInterlockedPutTail(PVBOXNETFLT_INTERLOCKED_SINGLE_LIST pList, PSINGLE_LIST_ENTRY pEntry) +{ + NdisAcquireSpinLock(&pList->Lock); + vboxNetFltWinPutTail(&pList->List, pEntry); + NdisReleaseSpinLock(&pList->Lock); +} + +DECLINLINE(void) vboxNetFltWinInterlockedPutHead(PVBOXNETFLT_INTERLOCKED_SINGLE_LIST pList, PSINGLE_LIST_ENTRY pEntry) +{ + NdisAcquireSpinLock(&pList->Lock); + vboxNetFltWinPutHead(&pList->List, pEntry); + NdisReleaseSpinLock(&pList->Lock); +} + +DECLINLINE(PSINGLE_LIST_ENTRY) vboxNetFltWinInterlockedGetHead(PVBOXNETFLT_INTERLOCKED_SINGLE_LIST pList) +{ + PSINGLE_LIST_ENTRY pEntry; + NdisAcquireSpinLock(&pList->Lock); + pEntry = vboxNetFltWinGetHead(&pList->List); + NdisReleaseSpinLock(&pList->Lock); + return pEntry; +} + +# if defined(DEBUG_NETFLT_PACKETS) || !defined(VBOX_LOOPBACK_USEFLAGS) +DECLINLINE(void) vboxNetFltWinLbPutSendPacket(PVBOXNETFLTINS pNetFlt, PNDIS_PACKET pPacket, bool bFromIntNet) +{ + PVBOXNETFLT_PKTRSVD_PT pSrv = (PVBOXNETFLT_PKTRSVD_PT)pPacket->ProtocolReserved; + pSrv->bFromIntNet = bFromIntNet; + vboxNetFltWinInterlockedPutHead(&pNetFlt->u.s.WinIf.SendPacketQueue, &pSrv->ListEntry); +} + +DECLINLINE(bool) vboxNetFltWinLbIsFromIntNet(PNDIS_PACKET pPacket) +{ + PVBOXNETFLT_PKTRSVD_PT pSrv = (PVBOXNETFLT_PKTRSVD_PT)pPacket->ProtocolReserved; + return pSrv->bFromIntNet; +} + +DECLINLINE(PNDIS_PACKET) vboxNetFltWinLbSearchLoopBack(PVBOXNETFLTINS pNetFlt, PNDIS_PACKET pPacket, bool bRemove) +{ + return vboxNetFltWinInterlockedSearchPacket(&pNetFlt->u.s.WinIf.SendPacketQueue, pPacket, VBOXNETFLT_PACKETMATCH_LENGTH, bRemove); +} + +DECLINLINE(PNDIS_PACKET) vboxNetFltWinLbSearchLoopBackBySG(PVBOXNETFLTINS pNetFlt, PINTNETSG pSG, bool bRemove) +{ + return vboxNetFltWinInterlockedSearchPacketBySG(&pNetFlt->u.s.WinIf.SendPacketQueue, pSG, VBOXNETFLT_PACKETMATCH_LENGTH, bRemove); +} + +DECLINLINE(bool) vboxNetFltWinLbRemoveSendPacket(PVBOXNETFLTINS pNetFlt, PNDIS_PACKET pPacket) +{ + PVBOXNETFLT_PKTRSVD_PT pSrv = (PVBOXNETFLT_PKTRSVD_PT)pPacket->ProtocolReserved; + bool bRet = vboxNetFltWinInterlockedSearchListEntry(&pNetFlt->u.s.WinIf.SendPacketQueue, &pSrv->ListEntry, true); +#ifdef DEBUG_misha + Assert(bRet == (pNetFlt->enmTrunkState == INTNETTRUNKIFSTATE_ACTIVE)); +#endif + return bRet; +} + +# endif + +#endif + +#ifdef DEBUG_misha +DECLHIDDEN(bool) vboxNetFltWinCheckMACs(PNDIS_PACKET pPacket, PRTMAC pDst, PRTMAC pSrc); +DECLHIDDEN(bool) vboxNetFltWinCheckMACsSG(PINTNETSG pSG, PRTMAC pDst, PRTMAC pSrc); +extern RTMAC g_vboxNetFltWinVerifyMACBroadcast; +extern RTMAC g_vboxNetFltWinVerifyMACGuest; + +# define VBOXNETFLT_LBVERIFY(_pnf, _p) \ + do { \ + Assert(!vboxNetFltWinCheckMACs(_p, NULL, &g_vboxNetFltWinVerifyMACGuest)); \ + Assert(!vboxNetFltWinCheckMACs(_p, NULL, &(_pnf)->u.s.MacAddr)); \ + } while (0) + +# define VBOXNETFLT_LBVERIFYSG(_pnf, _p) \ + do { \ + Assert(!vboxNetFltWinCheckMACsSG(_p, NULL, &g_vboxNetFltWinVerifyMACGuest)); \ + Assert(!vboxNetFltWinCheckMACsSG(_p, NULL, &(_pnf)->u.s.MacAddr)); \ + } while (0) + +#else +# define VBOXNETFLT_LBVERIFY(_pnf, _p) do { } while (0) +# define VBOXNETFLT_LBVERIFYSG(_pnf, _p) do { } while (0) +#endif + +/** initializes the list */ +#define INIT_SINGLE_LIST(_pList) \ + { \ + (_pList)->Head.Next = NULL; \ + (_pList)->pTail = &(_pList)->Head; \ + } + +/** initializes the list */ +#define INIT_INTERLOCKED_SINGLE_LIST(_pList) \ + do { \ + INIT_SINGLE_LIST(&(_pList)->List); \ + NdisAllocateSpinLock(&(_pList)->Lock); \ + } while (0) + +/** delete the packet queue */ +#define FINI_INTERLOCKED_SINGLE_LIST(_pList) \ + do { \ + Assert(vboxNetFltWinSListIsEmpty(&(_pList)->List)); \ + NdisFreeSpinLock(&(_pList)->Lock) \ + } while (0) + + +/************************************************************************** + * PVBOXNETFLTINS , WinIf reference/dereference (i.e. retain/release) API * + **************************************************************************/ + + +DECLHIDDEN(void) vboxNetFltWinWaitDereference(PVBOXNETFLT_WINIF_DEVICE pState); + +DECLINLINE(void) vboxNetFltWinReferenceModeNetFlt(PVBOXNETFLTINS pIns) +{ + ASMAtomicIncU32((volatile uint32_t *)&pIns->u.s.cModeNetFltRefs); +} + +DECLINLINE(void) vboxNetFltWinReferenceModePassThru(PVBOXNETFLTINS pIns) +{ + ASMAtomicIncU32((volatile uint32_t *)&pIns->u.s.cModePassThruRefs); +} + +DECLINLINE(void) vboxNetFltWinIncReferenceModeNetFlt(PVBOXNETFLTINS pIns, uint32_t v) +{ + ASMAtomicAddU32((volatile uint32_t *)&pIns->u.s.cModeNetFltRefs, v); +} + +DECLINLINE(void) vboxNetFltWinIncReferenceModePassThru(PVBOXNETFLTINS pIns, uint32_t v) +{ + ASMAtomicAddU32((volatile uint32_t *)&pIns->u.s.cModePassThruRefs, v); +} + +DECLINLINE(void) vboxNetFltWinDereferenceModeNetFlt(PVBOXNETFLTINS pIns) +{ + ASMAtomicDecU32((volatile uint32_t *)&pIns->u.s.cModeNetFltRefs); +} + +DECLINLINE(void) vboxNetFltWinDereferenceModePassThru(PVBOXNETFLTINS pIns) +{ + ASMAtomicDecU32((volatile uint32_t *)&pIns->u.s.cModePassThruRefs); +} + +DECLINLINE(void) vboxNetFltWinDecReferenceModeNetFlt(PVBOXNETFLTINS pIns, uint32_t v) +{ + Assert(v); + ASMAtomicAddU32((volatile uint32_t *)&pIns->u.s.cModeNetFltRefs, (uint32_t)(-((int32_t)v))); +} + +DECLINLINE(void) vboxNetFltWinDecReferenceModePassThru(PVBOXNETFLTINS pIns, uint32_t v) +{ + Assert(v); + ASMAtomicAddU32((volatile uint32_t *)&pIns->u.s.cModePassThruRefs, (uint32_t)(-((int32_t)v))); +} + +DECLINLINE(void) vboxNetFltWinSetPowerState(PVBOXNETFLT_WINIF_DEVICE pState, NDIS_DEVICE_POWER_STATE State) +{ + ASMAtomicUoWriteU32((volatile uint32_t *)&pState->PowerState, State); +} + +DECLINLINE(NDIS_DEVICE_POWER_STATE) vboxNetFltWinGetPowerState(PVBOXNETFLT_WINIF_DEVICE pState) +{ + return (NDIS_DEVICE_POWER_STATE)ASMAtomicUoReadU32((volatile uint32_t *)&pState->PowerState); +} + +DECLINLINE(void) vboxNetFltWinSetOpState(PVBOXNETFLT_WINIF_DEVICE pState, VBOXNETDEVOPSTATE State) +{ + ASMAtomicUoWriteU32((volatile uint32_t *)&pState->OpState, State); +} + +DECLINLINE(VBOXNETDEVOPSTATE) vboxNetFltWinGetOpState(PVBOXNETFLT_WINIF_DEVICE pState) +{ + return (VBOXNETDEVOPSTATE)ASMAtomicUoReadU32((volatile uint32_t *)&pState->OpState); +} + +DECLINLINE(bool) vboxNetFltWinDoReferenceDevice(PVBOXNETFLT_WINIF_DEVICE pState) +{ + if (vboxNetFltWinGetPowerState(pState) == NdisDeviceStateD0 && vboxNetFltWinGetOpState(pState) == kVBoxNetDevOpState_Initialized) + { + /** @todo r=bird: Since this is a volatile member, why don't you declare it as + * such and save yourself all the casting? */ + ASMAtomicIncU32((uint32_t volatile *)&pState->cReferences); + return true; + } + return false; +} + +#ifndef VBOXNETADP +DECLINLINE(bool) vboxNetFltWinDoReferenceDevices(PVBOXNETFLT_WINIF_DEVICE pState1, PVBOXNETFLT_WINIF_DEVICE pState2) +{ + if (vboxNetFltWinGetPowerState(pState1) == NdisDeviceStateD0 + && vboxNetFltWinGetOpState(pState1) == kVBoxNetDevOpState_Initialized + && vboxNetFltWinGetPowerState(pState2) == NdisDeviceStateD0 + && vboxNetFltWinGetOpState(pState2) == kVBoxNetDevOpState_Initialized) + { + ASMAtomicIncU32((uint32_t volatile *)&pState1->cReferences); + ASMAtomicIncU32((uint32_t volatile *)&pState2->cReferences); + return true; + } + return false; +} +#endif + +DECLINLINE(void) vboxNetFltWinDereferenceDevice(PVBOXNETFLT_WINIF_DEVICE pState) +{ + ASMAtomicDecU32((uint32_t volatile *)&pState->cReferences); + /** @todo r=bird: Add comment explaining why these cannot hit 0 or why + * reference are counted */ +} + +#ifndef VBOXNETADP +DECLINLINE(void) vboxNetFltWinDereferenceDevices(PVBOXNETFLT_WINIF_DEVICE pState1, PVBOXNETFLT_WINIF_DEVICE pState2) +{ + ASMAtomicDecU32((uint32_t volatile *)&pState1->cReferences); + ASMAtomicDecU32((uint32_t volatile *)&pState2->cReferences); +} +#endif + +DECLINLINE(void) vboxNetFltWinDecReferenceDevice(PVBOXNETFLT_WINIF_DEVICE pState, uint32_t v) +{ + Assert(v); + ASMAtomicAddU32((uint32_t volatile *)&pState->cReferences, (uint32_t)(-((int32_t)v))); +} + +#ifndef VBOXNETADP +DECLINLINE(void) vboxNetFltWinDecReferenceDevices(PVBOXNETFLT_WINIF_DEVICE pState1, PVBOXNETFLT_WINIF_DEVICE pState2, uint32_t v) +{ + ASMAtomicAddU32((uint32_t volatile *)&pState1->cReferences, (uint32_t)(-((int32_t)v))); + ASMAtomicAddU32((uint32_t volatile *)&pState2->cReferences, (uint32_t)(-((int32_t)v))); +} +#endif + +DECLINLINE(bool) vboxNetFltWinDoIncReferenceDevice(PVBOXNETFLT_WINIF_DEVICE pState, uint32_t v) +{ + Assert(v); + if (vboxNetFltWinGetPowerState(pState) == NdisDeviceStateD0 && vboxNetFltWinGetOpState(pState) == kVBoxNetDevOpState_Initialized) + { + ASMAtomicAddU32((uint32_t volatile *)&pState->cReferences, v); + return true; + } + return false; +} + +#ifndef VBOXNETADP +DECLINLINE(bool) vboxNetFltWinDoIncReferenceDevices(PVBOXNETFLT_WINIF_DEVICE pState1, PVBOXNETFLT_WINIF_DEVICE pState2, uint32_t v) +{ + if (vboxNetFltWinGetPowerState(pState1) == NdisDeviceStateD0 + && vboxNetFltWinGetOpState(pState1) == kVBoxNetDevOpState_Initialized + && vboxNetFltWinGetPowerState(pState2) == NdisDeviceStateD0 + && vboxNetFltWinGetOpState(pState2) == kVBoxNetDevOpState_Initialized) + { + ASMAtomicAddU32((uint32_t volatile *)&pState1->cReferences, v); + ASMAtomicAddU32((uint32_t volatile *)&pState2->cReferences, v); + return true; + } + return false; +} +#endif + + +DECLINLINE(bool) vboxNetFltWinReferenceWinIfNetFlt(PVBOXNETFLTINS pNetFlt, bool * pbNetFltActive) +{ + RTSpinlockAcquire((pNetFlt)->hSpinlock); +#ifndef VBOXNETADP + if (!vboxNetFltWinDoReferenceDevices(&pNetFlt->u.s.WinIf.MpState, &pNetFlt->u.s.WinIf.PtState)) +#else + if (!vboxNetFltWinDoReferenceDevice(&pNetFlt->u.s.WinIf.MpState)) +#endif + { + RTSpinlockRelease((pNetFlt)->hSpinlock); + *pbNetFltActive = false; + return false; + } + + if (pNetFlt->enmTrunkState != INTNETTRUNKIFSTATE_ACTIVE) + { + vboxNetFltWinReferenceModePassThru(pNetFlt); + RTSpinlockRelease((pNetFlt)->hSpinlock); + *pbNetFltActive = false; + return true; + } + + vboxNetFltRetain((pNetFlt), true /* fBusy */); + vboxNetFltWinReferenceModeNetFlt(pNetFlt); + RTSpinlockRelease((pNetFlt)->hSpinlock); + + *pbNetFltActive = true; + return true; +} + +DECLINLINE(bool) vboxNetFltWinIncReferenceWinIfNetFlt(PVBOXNETFLTINS pNetFlt, uint32_t v, bool *pbNetFltActive) +{ + uint32_t i; + + Assert(v); + if (!v) + { + *pbNetFltActive = false; + return false; + } + + RTSpinlockAcquire((pNetFlt)->hSpinlock); +#ifndef VBOXNETADP + if (!vboxNetFltWinDoIncReferenceDevices(&pNetFlt->u.s.WinIf.MpState, &pNetFlt->u.s.WinIf.PtState, v)) +#else + if (!vboxNetFltWinDoIncReferenceDevice(&pNetFlt->u.s.WinIf.MpState, v)) +#endif + { + RTSpinlockRelease(pNetFlt->hSpinlock); + *pbNetFltActive = false; + return false; + } + + if (pNetFlt->enmTrunkState != INTNETTRUNKIFSTATE_ACTIVE) + { + vboxNetFltWinIncReferenceModePassThru(pNetFlt, v); + + RTSpinlockRelease((pNetFlt)->hSpinlock); + *pbNetFltActive = false; + return true; + } + + vboxNetFltRetain(pNetFlt, true /* fBusy */); + + vboxNetFltWinIncReferenceModeNetFlt(pNetFlt, v); + + RTSpinlockRelease(pNetFlt->hSpinlock); + + /* we have marked it as busy, so can do the res references outside the lock */ + for (i = 0; i < v-1; i++) + { + vboxNetFltRetain(pNetFlt, true /* fBusy */); + } + + *pbNetFltActive = true; + + return true; +} + +DECLINLINE(void) vboxNetFltWinDecReferenceNetFlt(PVBOXNETFLTINS pNetFlt, uint32_t n) +{ + uint32_t i; + for (i = 0; i < n; i++) + { + vboxNetFltRelease(pNetFlt, true); + } + + vboxNetFltWinDecReferenceModeNetFlt(pNetFlt, n); +} + +DECLINLINE(void) vboxNetFltWinDereferenceNetFlt(PVBOXNETFLTINS pNetFlt) +{ + vboxNetFltRelease(pNetFlt, true); + + vboxNetFltWinDereferenceModeNetFlt(pNetFlt); +} + +DECLINLINE(void) vboxNetFltWinDecReferenceWinIf(PVBOXNETFLTINS pNetFlt, uint32_t v) +{ +#ifdef VBOXNETADP + vboxNetFltWinDecReferenceDevice(&pNetFlt->u.s.WinIf.MpState, v); +#else + vboxNetFltWinDecReferenceDevices(&pNetFlt->u.s.WinIf.MpState, &pNetFlt->u.s.WinIf.PtState, v); +#endif +} + +DECLINLINE(void) vboxNetFltWinDereferenceWinIf(PVBOXNETFLTINS pNetFlt) +{ +#ifdef VBOXNETADP + vboxNetFltWinDereferenceDevice(&pNetFlt->u.s.WinIf.MpState); +#else + vboxNetFltWinDereferenceDevices(&pNetFlt->u.s.WinIf.MpState, &pNetFlt->u.s.WinIf.PtState); +#endif +} + +DECLINLINE(bool) vboxNetFltWinIncReferenceWinIf(PVBOXNETFLTINS pNetFlt, uint32_t v) +{ + Assert(v); + if (!v) + { + return false; + } + + RTSpinlockAcquire(pNetFlt->hSpinlock); +#ifdef VBOXNETADP + if (vboxNetFltWinDoIncReferenceDevice(&pNetFlt->u.s.WinIf.MpState, v)) +#else + if (vboxNetFltWinDoIncReferenceDevices(&pNetFlt->u.s.WinIf.MpState, &pNetFlt->u.s.WinIf.PtState, v)) +#endif + { + RTSpinlockRelease(pNetFlt->hSpinlock); + return true; + } + + RTSpinlockRelease(pNetFlt->hSpinlock); + return false; +} + +DECLINLINE(bool) vboxNetFltWinReferenceWinIf(PVBOXNETFLTINS pNetFlt) +{ + RTSpinlockAcquire(pNetFlt->hSpinlock); +#ifdef VBOXNETADP + if (vboxNetFltWinDoReferenceDevice(&pNetFlt->u.s.WinIf.MpState)) +#else + if (vboxNetFltWinDoReferenceDevices(&pNetFlt->u.s.WinIf.MpState, &pNetFlt->u.s.WinIf.PtState)) +#endif + { + RTSpinlockRelease(pNetFlt->hSpinlock); + return true; + } + + RTSpinlockRelease(pNetFlt->hSpinlock); + return false; +} + +/*********************************************** + * methods for accessing the network card info * + ***********************************************/ + +DECLHIDDEN(NDIS_STATUS) vboxNetFltWinGetMacAddress(PVBOXNETFLTINS pNetFlt, PRTMAC pMac); +DECLHIDDEN(bool) vboxNetFltWinIsPromiscuous(PVBOXNETFLTINS pNetFlt); +DECLHIDDEN(NDIS_STATUS) vboxNetFltWinSetPromiscuous(PVBOXNETFLTINS pNetFlt, bool bYes); +DECLHIDDEN(NDIS_STATUS) vboxNetFltWinQueryPhysicalMedium(PVBOXNETFLTINS pNetFlt, NDIS_PHYSICAL_MEDIUM * pMedium); + +/********************* + * mem alloc API * + *********************/ + +DECLHIDDEN(NDIS_STATUS) vboxNetFltWinMemAlloc(PVOID* ppMemBuf, UINT cbLength); + +DECLHIDDEN(void) vboxNetFltWinMemFree(PVOID pMemBuf); + +/* convenience method used which allocates and initializes the PINTNETSG containing one + * segment referring the buffer of size cbBufSize + * the allocated PINTNETSG should be freed with the vboxNetFltWinMemFree. + * + * This is used when our ProtocolReceive callback is called and we have to return the indicated NDIS_PACKET + * on a callback exit. This is why we allocate the PINTNETSG and put the packet info there and enqueue it + * for the packet queue */ +DECLHIDDEN(NDIS_STATUS) vboxNetFltWinAllocSG(UINT cbBufSize, PINTNETSG *ppSG); + +/************************ + * WinIf init/fini API * + ************************/ +#if defined(VBOXNETADP) +DECLHIDDEN(NDIS_STATUS) vboxNetFltWinPtInitBind(PVBOXNETFLTINS *ppNetFlt, NDIS_HANDLE hMiniportAdapter, PNDIS_STRING pBindToMiniportName /* actually this is our miniport name*/, NDIS_HANDLE hWrapperConfigurationContext); +DECLHIDDEN(NDIS_STATUS) vboxNetFltWinPtInitWinIf(PVBOXNETFLTWIN pWinIf); +#else +DECLHIDDEN(NDIS_STATUS) vboxNetFltWinPtInitBind(PVBOXNETFLTINS *ppNetFlt, PNDIS_STRING pOurMiniportName, PNDIS_STRING pBindToMiniportName); +DECLHIDDEN(NDIS_STATUS) vboxNetFltWinPtInitWinIf(PVBOXNETFLTWIN pWinIf, PNDIS_STRING pOurDeviceName); +#endif + +DECLHIDDEN(VOID) vboxNetFltWinPtFiniWinIf(PVBOXNETFLTWIN pWinIf); + +/************************************ + * Execute Job at passive level API * + ************************************/ + +typedef VOID (*PFNVBOXNETFLT_JOB_ROUTINE) (PVOID pContext); + +DECLHIDDEN(VOID) vboxNetFltWinJobSynchExecAtPassive(PFNVBOXNETFLT_JOB_ROUTINE pfnRoutine, PVOID pContext); + +/******************************* + * Ndis Packets processing API * + *******************************/ +DECLHIDDEN(PNDIS_PACKET) vboxNetFltWinNdisPacketFromSG(PVBOXNETFLTINS pNetFlt, PINTNETSG pSG, PVOID pBufToFree, bool bToWire, bool bCopyMemory); + +DECLHIDDEN(void) vboxNetFltWinFreeSGNdisPacket(PNDIS_PACKET pPacket, bool bFreeMem); + +#ifdef DEBUG_NETFLT_PACKETS +#define DBG_CHECK_PACKETS(_p1, _p2) \ + { \ + bool _b = vboxNetFltWinMatchPackets(_p1, _p2, -1); \ + Assert(_b); \ + } + +#define DBG_CHECK_PACKET_AND_SG(_p, _sg) \ + { \ + bool _b = vboxNetFltWinMatchPacketAndSG(_p, _sg, -1); \ + Assert(_b); \ + } + +#define DBG_CHECK_SGS(_sg1, _sg2) \ + { \ + bool _b = vboxNetFltWinMatchSGs(_sg1, _sg2, -1); \ + Assert(_b); \ + } + +#else +#define DBG_CHECK_PACKETS(_p1, _p2) +#define DBG_CHECK_PACKET_AND_SG(_p, _sg) +#define DBG_CHECK_SGS(_sg1, _sg2) +#endif + +/** + * Ndis loops back broadcast packets posted to the wire by IntNet + * This routine is used in the mechanism of preventing this looping + * + * @param pAdapt + * @param pPacket + * @param bOnRecv true is we are receiving the packet from the wire + * false otherwise (i.e. the packet is from the host) + * + * @return true if the packet is a looped back one, false otherwise + */ +#ifdef VBOX_LOOPBACK_USEFLAGS +DECLINLINE(bool) vboxNetFltWinIsLoopedBackPacket(PNDIS_PACKET pPacket) +{ + return (NdisGetPacketFlags(pPacket) & g_fPacketIsLoopedBack) == g_fPacketIsLoopedBack; +} +#endif + +/************************************************************** + * utility methods for ndis packet creation/initialization * + **************************************************************/ + +#define VBOXNETFLT_OOB_INIT(_p) \ + { \ + NdisZeroMemory(NDIS_OOB_DATA_FROM_PACKET(_p), sizeof(NDIS_PACKET_OOB_DATA)); \ + NDIS_SET_PACKET_HEADER_SIZE(_p, VBOXNETFLT_PACKET_ETHEADER_SIZE); \ + } + +#ifndef VBOXNETADP + +DECLINLINE(NDIS_STATUS) vboxNetFltWinCopyPacketInfoOnRecv(PNDIS_PACKET pDstPacket, PNDIS_PACKET pSrcPacket, bool bForceStatusResources) +{ + NDIS_STATUS Status = bForceStatusResources ? NDIS_STATUS_RESOURCES : NDIS_GET_PACKET_STATUS(pSrcPacket); + NDIS_SET_PACKET_STATUS(pDstPacket, Status); + + NDIS_PACKET_FIRST_NDIS_BUFFER(pDstPacket) = NDIS_PACKET_FIRST_NDIS_BUFFER(pSrcPacket); + NDIS_PACKET_LAST_NDIS_BUFFER(pDstPacket) = NDIS_PACKET_LAST_NDIS_BUFFER(pSrcPacket); + + NdisGetPacketFlags(pDstPacket) = NdisGetPacketFlags(pSrcPacket); + + NDIS_SET_ORIGINAL_PACKET(pDstPacket, NDIS_GET_ORIGINAL_PACKET(pSrcPacket)); + NDIS_SET_PACKET_HEADER_SIZE(pDstPacket, NDIS_GET_PACKET_HEADER_SIZE(pSrcPacket)); + + return Status; +} + +DECLINLINE(void) vboxNetFltWinCopyPacketInfoOnSend(PNDIS_PACKET pDstPacket, PNDIS_PACKET pSrcPacket) +{ + NDIS_PACKET_FIRST_NDIS_BUFFER(pDstPacket) = NDIS_PACKET_FIRST_NDIS_BUFFER(pSrcPacket); + NDIS_PACKET_LAST_NDIS_BUFFER(pDstPacket) = NDIS_PACKET_LAST_NDIS_BUFFER(pSrcPacket); + + NdisGetPacketFlags(pDstPacket) = NdisGetPacketFlags(pSrcPacket); + + NdisMoveMemory(NDIS_OOB_DATA_FROM_PACKET(pDstPacket), + NDIS_OOB_DATA_FROM_PACKET(pSrcPacket), + sizeof (NDIS_PACKET_OOB_DATA)); + + NdisIMCopySendPerPacketInfo(pDstPacket, pSrcPacket); + + PVOID pMediaSpecificInfo = NULL; + UINT fMediaSpecificInfoSize = 0; + + NDIS_GET_PACKET_MEDIA_SPECIFIC_INFO(pSrcPacket, &pMediaSpecificInfo, &fMediaSpecificInfoSize); + + if (pMediaSpecificInfo || fMediaSpecificInfoSize) + { + NDIS_SET_PACKET_MEDIA_SPECIFIC_INFO(pDstPacket, pMediaSpecificInfo, fMediaSpecificInfoSize); + } +} + +DECLHIDDEN(NDIS_STATUS) vboxNetFltWinPrepareSendPacket(PVBOXNETFLTINS pNetFlt, PNDIS_PACKET pPacket, PNDIS_PACKET *ppMyPacket); +DECLHIDDEN(NDIS_STATUS) vboxNetFltWinPrepareRecvPacket(PVBOXNETFLTINS pNetFlt, PNDIS_PACKET pPacket, PNDIS_PACKET *ppMyPacket, bool bDpr); +#endif + +DECLHIDDEN(void) vboxNetFltWinSleep(ULONG milis); + +#define MACS_EQUAL(_m1, _m2) \ + ((_m1).au16[0] == (_m2).au16[0] \ + && (_m1).au16[1] == (_m2).au16[1] \ + && (_m1).au16[2] == (_m2).au16[2]) + + +DECLHIDDEN(NDIS_STATUS) vboxNetFltWinDetachFromInterface(PVBOXNETFLTINS pNetFlt, bool bOnUnbind); +DECLHIDDEN(NDIS_STATUS) vboxNetFltWinCopyString(PNDIS_STRING pDst, PNDIS_STRING pSrc); + + +/** + * Sets the enmState member atomically. + * + * Used for all updates. + * + * @param pThis The instance. + * @param enmNewState The new value. + */ +DECLINLINE(void) vboxNetFltWinSetWinIfState(PVBOXNETFLTINS pNetFlt, VBOXNETFLT_WINIFSTATE enmNewState) +{ + ASMAtomicWriteU32((uint32_t volatile *)&pNetFlt->u.s.WinIf.enmState, enmNewState); +} + +/** + * Gets the enmState member atomically. + * + * Used for all reads. + * + * @returns The enmState value. + * @param pThis The instance. + */ +DECLINLINE(VBOXNETFLT_WINIFSTATE) vboxNetFltWinGetWinIfState(PVBOXNETFLTINS pNetFlt) +{ + return (VBOXNETFLT_WINIFSTATE)ASMAtomicUoReadU32((uint32_t volatile *)&pNetFlt->u.s.WinIf.enmState); +} + +/* reference the driver module to prevent driver unload */ +DECLHIDDEN(void) vboxNetFltWinDrvReference(); +/* dereference the driver module to prevent driver unload */ +DECLHIDDEN(void) vboxNetFltWinDrvDereference(); + + +#ifndef VBOXNETADP +# define VBOXNETFLT_PROMISCUOUS_SUPPORTED(_pNetFlt) (!(_pNetFlt)->fDisablePromiscuous) +#else +# define STATISTIC_INCREASE(_s) ASMAtomicIncU32((uint32_t volatile *)&(_s)); + +DECLHIDDEN(void) vboxNetFltWinGenerateMACAddress(RTMAC *pMac); +DECLHIDDEN(int) vboxNetFltWinMAC2NdisString(RTMAC *pMac, PNDIS_STRING pNdisString); +DECLHIDDEN(int) vboxNetFltWinMACFromNdisString(RTMAC *pMac, PNDIS_STRING pNdisString); + +#endif +#endif /* !VBOX_INCLUDED_SRC_VBoxNetFlt_win_drv_VBoxNetFltRt_win_h */ diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetLwf-win.cpp b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetLwf-win.cpp new file mode 100644 index 00000000..ce23890d --- /dev/null +++ b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetLwf-win.cpp @@ -0,0 +1,2723 @@ +/* $Id: VBoxNetLwf-win.cpp $ */ +/** @file + * VBoxNetLwf-win.cpp - NDIS6 Bridged Networking Driver, Windows-specific code. + */ +/* + * Copyright (C) 2014-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * 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. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE 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. + */ +#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 (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; + } + if (cbAlignedFrame > pSG->cbTotal) + { + Log4(("vboxNetLwfWinSGtoNB: padding %d-byte packet with %d zero bytes", pSG->cbTotal, cbAlignedFrame - pSG->cbTotal)); + NdisZeroMemory(pDst, cbAlignedFrame - pSG->cbTotal); + } + Log4(("vboxNetLwfWinSGtoNB: allocated NBL+NB 0x%p\n", pBufList)); + pBufList->SourceHandle = pModule->hFilter; + } + else + { + LogError(("vboxNetLwfWinSGtoNB: failed to obtain the buffer pointer (size=%u)\n", pSG->cbTotal)); + NdisAdvanceNetBufferDataStart(pBuffer, cbAlignedFrame, false, NULL); /** @todo why bother? */ + NdisFreeNetBufferList(pBufList); + pBufList = NULL; + } + } + else + { + LogError(("vboxNetLwfWinSGtoNB: NdisRetreatNetBufferDataStart failed with 0x%x (size=%u)\n", Status, pSG->cbTotal)); + NdisFreeNetBufferList(pBufList); + pBufList = NULL; + } +# else /* !VBOXNETLWF_FIXED_SIZE_POOLS */ + PNET_BUFFER_LIST pBufList = NULL; + ULONG cbMdl = VBOXNETLWF_MAX_FRAME_SIZE(pSG->cbTotal); + ULONG uDataOffset = cbMdl - pSG->cbTotal; + PUCHAR pDataBuf = (PUCHAR)NdisAllocateMemoryWithTagPriority(pModule->hFilter, cbMdl, + VBOXNETLWF_MEM_TAG, NormalPoolPriority); + if (pDataBuf) + { + Log4(("vboxNetLwfWinSGtoNB: allocated data buffer (cb=%u) 0x%p\n", cbMdl, pDataBuf)); + PMDL pMdl = NdisAllocateMdl(pModule->hFilter, pDataBuf, cbMdl); + if (!pMdl) + { + NdisFreeMemory(pDataBuf, 0, 0); + Log4(("vboxNetLwfWinSGtoNB: freed data buffer 0x%p\n", pDataBuf)); + LogError(("vboxNetLwfWinSGtoNB: failed to allocate an MDL (cb=%u)\n", cbMdl)); + LogFlow(("<==vboxNetLwfWinSGtoNB: return NULL\n")); + return NULL; + } + PUCHAR pDst = pDataBuf + uDataOffset; + for (int i = 0; i < pSG->cSegsUsed; i++) + { + NdisMoveMemory(pDst, pSG->aSegs[i].pv, pSG->aSegs[i].cb); + pDst += pSG->aSegs[i].cb; + } + pBufList = NdisAllocateNetBufferAndNetBufferList(pModule->hPool, + 0 /* ContextSize */, + 0 /* ContextBackFill */, + pMdl, + uDataOffset, + pSG->cbTotal); + if (pBufList) + { + Log4(("vboxNetLwfWinSGtoNB: allocated NBL+NB 0x%p\n", pBufList)); + pBufList->SourceHandle = pModule->hFilter; + /** @todo Do we need to initialize anything else? */ + } + else + { + LogError(("vboxNetLwfWinSGtoNB: failed to allocate an NBL+NB\n")); + vboxNetLwfWinFreeMdlChain(pMdl); + } + } + else + { + LogError(("vboxNetLwfWinSGtoNB: failed to allocate data buffer (size=%u)\n", cbMdl)); + } +# endif /* !VBOXNETLWF_FIXED_SIZE_POOLS */ + +#endif /* !VBOXNETLWF_SYNC_SEND */ + LogFlow(("<==vboxNetLwfWinSGtoNB: return %p\n", pBufList)); + return pBufList; +} + +/** + * @note vboxNetAdpWinNBtoSG() is a copy of this code. + */ +static PINTNETSG vboxNetLwfWinNBtoSG(PVBOXNETLWF_MODULE pModule, PNET_BUFFER pNetBuf) +{ + ULONG cbPacket = NET_BUFFER_DATA_LENGTH(pNetBuf); + ULONG cSegs = vboxNetLwfWinCalcSegments(pNetBuf); + /* Allocate and initialize SG */ + PINTNETSG pSG = (PINTNETSG)NdisAllocateMemoryWithTagPriority(pModule->hFilter, + RT_UOFFSETOF_DYN(INTNETSG, aSegs[cSegs]), + VBOXNETLWF_MEM_TAG, + NormalPoolPriority); + AssertReturn(pSG, pSG); + Log4(("vboxNetLwfWinNBtoSG: allocated SG 0x%p\n", pSG)); + IntNetSgInitTempSegs(pSG, cbPacket /*cbTotal*/, cSegs, cSegs /*cSegsUsed*/); + + ULONG uOffset = NET_BUFFER_CURRENT_MDL_OFFSET(pNetBuf); + cSegs = 0; + for (PMDL pMdl = NET_BUFFER_CURRENT_MDL(pNetBuf); + pMdl != NULL && cbPacket > 0; + pMdl = NDIS_MDL_LINKAGE(pMdl)) + { + ULONG cbSrc = MmGetMdlByteCount(pMdl); + if (cbSrc == 0) + continue; /* Skip empty MDLs (see @bugref{9233}) */ + + PUCHAR pSrc = (PUCHAR)MmGetSystemAddressForMdlSafe(pMdl, LowPagePriority); + if (!pSrc) + { + vboxNetLwfWinDestroySG(pSG); + return NULL; + } + + /* Handle the offset in the current (which is the first for us) MDL */ + if (uOffset) + { + if (uOffset < cbSrc) + { + pSrc += uOffset; + cbSrc -= uOffset; + uOffset = 0; + } + else + { + /* This is an invalid MDL chain */ + vboxNetLwfWinDestroySG(pSG); + return NULL; + } + } + + /* Do not read the last MDL beyond packet's end */ + if (cbSrc > cbPacket) + cbSrc = cbPacket; + + Assert(cSegs < pSG->cSegsAlloc); + pSG->aSegs[cSegs].pv = pSrc; + pSG->aSegs[cSegs].cb = cbSrc; + pSG->aSegs[cSegs].Phys = NIL_RTHCPHYS; + cSegs++; + cbPacket -= cbSrc; + } + + Assert(cbPacket == 0); + Assert(cSegs <= pSG->cSegsUsed); + + /* Update actual segment count in case we used fewer than anticipated. */ + pSG->cSegsUsed = (uint16_t)cSegs; + + return pSG; +} + +VOID vboxNetLwfWinStatus(IN NDIS_HANDLE hModuleCtx, IN PNDIS_STATUS_INDICATION pIndication) +{ + LogFlow(("==>vboxNetLwfWinStatus: module=%p\n", hModuleCtx)); + PVBOXNETLWF_MODULE pModuleCtx = (PVBOXNETLWF_MODULE)hModuleCtx; + Log(("vboxNetLwfWinStatus: Got status indication: %s\n", vboxNetLwfWinStatusToText(pIndication->StatusCode))); + switch (pIndication->StatusCode) + { + case NDIS_STATUS_PACKET_FILTER: + vboxNetLwfWinDumpFilterTypes(*(ULONG*)pIndication->StatusBuffer); + vboxNetLwfWinOverridePacketFiltersUp(pModuleCtx, (ULONG*)pIndication->StatusBuffer); + Log(("vboxNetLwfWinStatus: Reporting status: %s\n", vboxNetLwfWinStatusToText(pIndication->StatusCode))); + vboxNetLwfWinDumpFilterTypes(*(ULONG*)pIndication->StatusBuffer); + break; + case NDIS_STATUS_TASK_OFFLOAD_CURRENT_CONFIG: + Log5(("vboxNetLwfWinStatus: offloading currently set to:\n")); + vboxNetLwfWinDumpOffloadSettings((PNDIS_OFFLOAD)pIndication->StatusBuffer); + vboxNetLwfWinUpdateSavedOffloadConfig(pModuleCtx, (PNDIS_OFFLOAD)pIndication->StatusBuffer); + if (ASMAtomicReadBool(&pModuleCtx->fActive)) + vboxNetLwfWinDisableOffloading((PNDIS_OFFLOAD)pIndication->StatusBuffer); + Log5(("vboxNetLwfWinStatus: reporting offloading up as:\n")); + vboxNetLwfWinDumpOffloadSettings((PNDIS_OFFLOAD)pIndication->StatusBuffer); + break; + } + NdisFIndicateStatus(pModuleCtx->hFilter, pIndication); + LogFlow(("<==vboxNetLwfWinStatus\n")); +} + +static bool vboxNetLwfWinForwardToIntNet(PVBOXNETLWF_MODULE pModuleCtx, PNET_BUFFER_LIST pBufLists, uint32_t fSrc) +{ + /* We must not forward anything to the trunk unless it is ready to receive. */ + if (!ASMAtomicReadBool(&pModuleCtx->fActive)) + { + Log(("vboxNetLwfWinForwardToIntNet: trunk is inactive, won't forward\n")); + return false; + } + /* Some NPF protocols make NDIS to loop back packets at miniport level, we must ignore those. */ + if (NdisTestNblFlag(pBufLists, NDIS_NBL_FLAGS_IS_LOOPBACK_PACKET)) + { + if (pBufLists->SourceHandle == pModuleCtx->hFilter && !pModuleCtx->fPassVmTrafficToHost) + { + /* Drop the packets we've injected. */ + vboxNetLwfWinDumpPackets("vboxNetLwfWinForwardToIntNet: dropping loopback", pBufLists); + return true; + } + vboxNetLwfWinDumpPackets("vboxNetLwfWinForwardToIntNet: passing through loopback", pBufLists); + return false; + } + + AssertReturn(pModuleCtx->pNetFlt, false); + AssertReturn(pModuleCtx->pNetFlt->pSwitchPort, false); + AssertReturn(pModuleCtx->pNetFlt->pSwitchPort->pfnRecv, false); + LogFlow(("==>vboxNetLwfWinForwardToIntNet: module=%p\n", pModuleCtx)); + Assert(pBufLists); /* The chain must contain at least one list */ + Assert(NET_BUFFER_LIST_NEXT_NBL(pBufLists) == NULL); /* The caller is supposed to unlink the list from the chain */ + /* + * Even if NBL contains more than one buffer we are prepared to deal with it. + * When any of buffers should not be dropped we keep the whole list. It is + * better to leak some "unexpected" packets to the wire/host than to loose any. + */ + bool fDropIt = false; + bool fDontDrop = false; + int nLists = 0; + for (PNET_BUFFER_LIST pList = pBufLists; pList; pList = NET_BUFFER_LIST_NEXT_NBL(pList)) + { + int nBuffers = 0; + nLists++; + for (PNET_BUFFER pBuf = NET_BUFFER_LIST_FIRST_NB(pList); pBuf; pBuf = NET_BUFFER_NEXT_NB(pBuf)) + { + nBuffers++; + PINTNETSG pSG = vboxNetLwfWinNBtoSG(pModuleCtx, pBuf); + if (pSG) + { + vboxNetLwfWinDumpPacket(pSG, (fSrc & INTNETTRUNKDIR_WIRE)?"intnet <-- wire":"intnet <-- host"); + /* A bit paranoid, but we do not use any locks, so... */ + if (ASMAtomicReadBool(&pModuleCtx->fActive)) + if (pModuleCtx->pNetFlt->pSwitchPort->pfnRecv(pModuleCtx->pNetFlt->pSwitchPort, NULL, pSG, fSrc)) + fDropIt = true; + else + fDontDrop = true; + vboxNetLwfWinDestroySG(pSG); + } + } + Log(("vboxNetLwfWinForwardToIntNet: list=%d buffers=%d\n", nLists, nBuffers)); + } + Log(("vboxNetLwfWinForwardToIntNet: lists=%d drop=%s don't=%s\n", nLists, fDropIt ? "true":"false", fDontDrop ? "true":"false")); + + /* If the host (and the user) wants to see all packets we must not drop any. */ + if (pModuleCtx->fPassVmTrafficToHost && vboxNetLwfWinIsPromiscuous(pModuleCtx)) + fDropIt = false; + + LogFlow(("<==vboxNetLwfWinForwardToIntNet: return '%s'\n", + fDropIt ? (fDontDrop ? "do not drop (some)" : "drop it") : "do not drop (any)")); + return fDropIt && !fDontDrop; /* Drop the list if ALL its buffers are being dropped! */ +} + +DECLINLINE(bool) vboxNetLwfWinIsRunning(PVBOXNETLWF_MODULE pModule) +{ + Log(("vboxNetLwfWinIsRunning: state=%d\n", ASMAtomicReadU32(&pModule->enmState))); + return ASMAtomicReadU32(&pModule->enmState) == LwfState_Running; +} + +VOID vboxNetLwfWinSendNetBufferLists(IN NDIS_HANDLE hModuleCtx, IN PNET_BUFFER_LIST pBufLists, IN NDIS_PORT_NUMBER nPort, IN ULONG fFlags) +{ + LogFlow(("==>vboxNetLwfWinSendNetBufferLists: module=%p\n", hModuleCtx)); + PVBOXNETLWF_MODULE pModule = (PVBOXNETLWF_MODULE)hModuleCtx; + vboxNetLwfWinDumpPackets("vboxNetLwfWinSendNetBufferLists: got", pBufLists); + + if (!ASMAtomicReadBool(&pModule->fActive)) + { + /* + * The trunk is inactive, jusp pass along all packets to the next + * underlying driver. + */ + NdisFSendNetBufferLists(pModule->hFilter, pBufLists, nPort, fFlags); + return; + } + + if (vboxNetLwfWinIsRunning(pModule)) + { + PNET_BUFFER_LIST pNext = NULL; + PNET_BUFFER_LIST pDropHead = NULL; + PNET_BUFFER_LIST pDropTail = NULL; + PNET_BUFFER_LIST pPassHead = NULL; + PNET_BUFFER_LIST pPassTail = NULL; + for (PNET_BUFFER_LIST pList = pBufLists; pList; pList = pNext) + { + pNext = NET_BUFFER_LIST_NEXT_NBL(pList); + NET_BUFFER_LIST_NEXT_NBL(pList) = NULL; /* Unlink */ + if (vboxNetLwfWinForwardToIntNet(pModule, pList, INTNETTRUNKDIR_HOST)) + { + NET_BUFFER_LIST_STATUS(pList) = NDIS_STATUS_SUCCESS; + if (pDropHead) + { + NET_BUFFER_LIST_NEXT_NBL(pDropTail) = pList; + pDropTail = pList; + } + else + pDropHead = pDropTail = pList; + } + else + { + if (pPassHead) + { + NET_BUFFER_LIST_NEXT_NBL(pPassTail) = pList; + pPassTail = pList; + } + else + pPassHead = pPassTail = pList; + } + } + Assert((pBufLists == pPassHead) || (pBufLists == pDropHead)); + if (pPassHead) + { + vboxNetLwfWinDumpPackets("vboxNetLwfWinSendNetBufferLists: passing down", pPassHead); + NdisFSendNetBufferLists(pModule->hFilter, pBufLists, nPort, fFlags); + } + if (pDropHead) + { + vboxNetLwfWinDumpPackets("vboxNetLwfWinSendNetBufferLists: consumed", pDropHead); + NdisFSendNetBufferListsComplete(pModule->hFilter, pDropHead, + fFlags & NDIS_SEND_FLAGS_DISPATCH_LEVEL ? NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL : 0); + } + } + else + { + for (PNET_BUFFER_LIST pList = pBufLists; pList; pList = NET_BUFFER_LIST_NEXT_NBL(pList)) + { + NET_BUFFER_LIST_STATUS(pList) = NDIS_STATUS_PAUSED; + } + vboxNetLwfWinDumpPackets("vboxNetLwfWinSendNetBufferLists: consumed", pBufLists); + NdisFSendNetBufferListsComplete(pModule->hFilter, pBufLists, + fFlags & NDIS_SEND_FLAGS_DISPATCH_LEVEL ? NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL : 0); + + } + LogFlow(("<==vboxNetLwfWinSendNetBufferLists\n")); +} + +VOID vboxNetLwfWinSendNetBufferListsComplete(IN NDIS_HANDLE hModuleCtx, IN PNET_BUFFER_LIST pBufLists, IN ULONG fFlags) +{ + LogFlow(("==>vboxNetLwfWinSendNetBufferListsComplete: module=%p\n", hModuleCtx)); + PVBOXNETLWF_MODULE pModule = (PVBOXNETLWF_MODULE)hModuleCtx; + PNET_BUFFER_LIST pList = pBufLists; + PNET_BUFFER_LIST pNextList; + PNET_BUFFER_LIST pPrevList = NULL; + while (pList) + { + pNextList = NET_BUFFER_LIST_NEXT_NBL(pList); + if (pList->SourceHandle == pModule->hFilter) + { + /* We allocated this NET_BUFFER_LIST, let's free it up */ + Assert(NET_BUFFER_LIST_FIRST_NB(pList)); + Assert(NET_BUFFER_FIRST_MDL(NET_BUFFER_LIST_FIRST_NB(pList))); + /* + * All our NBLs hold a single NB each, no need to iterate over a list. + * There is no need to free an associated NB explicitly either, as it was + * preallocated with NBL structure. + */ + Assert(!NET_BUFFER_NEXT_NB(NET_BUFFER_LIST_FIRST_NB(pList))); + vboxNetLwfWinFreeMdlChain(NET_BUFFER_FIRST_MDL(NET_BUFFER_LIST_FIRST_NB(pList))); + /* Unlink this list from the chain */ + if (pPrevList) + NET_BUFFER_LIST_NEXT_NBL(pPrevList) = pNextList; + else + pBufLists = pNextList; + Log(("vboxNetLwfWinSendNetBufferListsComplete: our list %p, next=%p, previous=%p, head=%p\n", pList, pNextList, pPrevList, pBufLists)); + NdisFreeNetBufferList(pList); +#ifdef VBOXNETLWF_SYNC_SEND + Log4(("vboxNetLwfWinSendNetBufferListsComplete: freed NBL+NB 0x%p\n", pList)); + KeSetEvent(&pModule->EventWire, 0, FALSE); +#else /* !VBOXNETLWF_SYNC_SEND */ + Log4(("vboxNetLwfWinSendNetBufferListsComplete: freed NBL+NB+MDL+Data 0x%p\n", pList)); + Assert(ASMAtomicReadS32(&pModule->cPendingBuffers) > 0); + if (ASMAtomicDecS32(&pModule->cPendingBuffers) == 0) + NdisSetEvent(&pModule->EventSendComplete); +#endif /* !VBOXNETLWF_SYNC_SEND */ + } + else + { + pPrevList = pList; + Log(("vboxNetLwfWinSendNetBufferListsComplete: passing list %p, next=%p, previous=%p, head=%p\n", pList, pNextList, pPrevList, pBufLists)); + } + pList = pNextList; + } + if (pBufLists) + { + /* There are still lists remaining in the chain, pass'em up */ + NdisFSendNetBufferListsComplete(pModule->hFilter, pBufLists, fFlags); + } + LogFlow(("<==vboxNetLwfWinSendNetBufferListsComplete\n")); +} + +VOID vboxNetLwfWinReceiveNetBufferLists(IN NDIS_HANDLE hModuleCtx, + IN PNET_BUFFER_LIST pBufLists, + IN NDIS_PORT_NUMBER nPort, + IN ULONG nBufLists, + IN ULONG fFlags) +{ + /// @todo Do we need loopback handling? + LogFlow(("==>vboxNetLwfWinReceiveNetBufferLists: module=%p\n", hModuleCtx)); + PVBOXNETLWF_MODULE pModule = (PVBOXNETLWF_MODULE)hModuleCtx; + vboxNetLwfWinDumpPackets("vboxNetLwfWinReceiveNetBufferLists: got", pBufLists); + + if (!ASMAtomicReadBool(&pModule->fActive)) + { + /* + * The trunk is inactive, just pass along all packets to the next + * overlying driver. + */ + NdisFIndicateReceiveNetBufferLists(pModule->hFilter, pBufLists, nPort, nBufLists, fFlags); + LogFlow(("<==vboxNetLwfWinReceiveNetBufferLists: inactive trunk\n")); + return; + } + + if (vboxNetLwfWinIsRunning(pModule)) + { + if (NDIS_TEST_RECEIVE_CANNOT_PEND(fFlags)) + { + for (PNET_BUFFER_LIST pList = pBufLists; pList; pList = NET_BUFFER_LIST_NEXT_NBL(pList)) + { + PNET_BUFFER_LIST pNext = NET_BUFFER_LIST_NEXT_NBL(pList); + NET_BUFFER_LIST_NEXT_NBL(pList) = NULL; /* Unlink temporarily */ + if (!vboxNetLwfWinForwardToIntNet(pModule, pList, INTNETTRUNKDIR_WIRE)) + { + vboxNetLwfWinDumpPackets("vboxNetLwfWinReceiveNetBufferLists: passing up", pList); + NdisFIndicateReceiveNetBufferLists(pModule->hFilter, pList, nPort, nBufLists, fFlags); + } + NET_BUFFER_LIST_NEXT_NBL(pList) = pNext; /* Restore the link */ + } + } + else + { + /* We collect dropped NBLs in a separate list in order to "return" them. */ + PNET_BUFFER_LIST pNext = NULL; + PNET_BUFFER_LIST pDropHead = NULL; + PNET_BUFFER_LIST pDropTail = NULL; + PNET_BUFFER_LIST pPassHead = NULL; + PNET_BUFFER_LIST pPassTail = NULL; + ULONG nDrop = 0, nPass = 0; + for (PNET_BUFFER_LIST pList = pBufLists; pList; pList = pNext) + { + pNext = NET_BUFFER_LIST_NEXT_NBL(pList); + NET_BUFFER_LIST_NEXT_NBL(pList) = NULL; /* Unlink */ + if (vboxNetLwfWinForwardToIntNet(pModule, pList, INTNETTRUNKDIR_WIRE)) + { + if (nDrop++) + { + NET_BUFFER_LIST_NEXT_NBL(pDropTail) = pList; + pDropTail = pList; + } + else + pDropHead = pDropTail = pList; + } + else + { + if (nPass++) + { + NET_BUFFER_LIST_NEXT_NBL(pPassTail) = pList; + pPassTail = pList; + } + else + pPassHead = pPassTail = pList; + } + } + Assert((pBufLists == pPassHead) || (pBufLists == pDropHead)); + Assert(nDrop + nPass == nBufLists); + if (pPassHead) + { + vboxNetLwfWinDumpPackets("vboxNetLwfWinReceiveNetBufferLists: passing up", pPassHead); + NdisFIndicateReceiveNetBufferLists(pModule->hFilter, pPassHead, nPort, nPass, fFlags); + } + if (pDropHead) + { + vboxNetLwfWinDumpPackets("vboxNetLwfWinReceiveNetBufferLists: consumed", pDropHead); + NdisFReturnNetBufferLists(pModule->hFilter, pDropHead, + fFlags & NDIS_RECEIVE_FLAGS_DISPATCH_LEVEL ? NDIS_RETURN_FLAGS_DISPATCH_LEVEL : 0); + } + } + + } + else + { + vboxNetLwfWinDumpPackets("vboxNetLwfWinReceiveNetBufferLists: consumed", pBufLists); + if ((fFlags & NDIS_RECEIVE_FLAGS_RESOURCES) == 0) + NdisFReturnNetBufferLists(pModule->hFilter, pBufLists, + fFlags & NDIS_RECEIVE_FLAGS_DISPATCH_LEVEL ? NDIS_RETURN_FLAGS_DISPATCH_LEVEL : 0); + } + LogFlow(("<==vboxNetLwfWinReceiveNetBufferLists\n")); +} + +VOID vboxNetLwfWinReturnNetBufferLists(IN NDIS_HANDLE hModuleCtx, IN PNET_BUFFER_LIST pBufLists, IN ULONG fFlags) +{ + LogFlow(("==>vboxNetLwfWinReturnNetBufferLists: module=%p\n", hModuleCtx)); + PVBOXNETLWF_MODULE pModule = (PVBOXNETLWF_MODULE)hModuleCtx; + PNET_BUFFER_LIST pList = pBufLists; + PNET_BUFFER_LIST pNextList; + PNET_BUFFER_LIST pPrevList = NULL; + /** @todo Move common part into a separate function to be used by vboxNetLwfWinSendNetBufferListsComplete() as well */ + while (pList) + { + pNextList = NET_BUFFER_LIST_NEXT_NBL(pList); + if (pList->SourceHandle == pModule->hFilter) + { + /* We allocated this NET_BUFFER_LIST, let's free it up */ + Assert(NET_BUFFER_LIST_FIRST_NB(pList)); + Assert(NET_BUFFER_FIRST_MDL(NET_BUFFER_LIST_FIRST_NB(pList))); + /* + * All our NBLs hold a single NB each, no need to iterate over a list. + * There is no need to free an associated NB explicitly either, as it was + * preallocated with NBL structure. + */ + vboxNetLwfWinFreeMdlChain(NET_BUFFER_FIRST_MDL(NET_BUFFER_LIST_FIRST_NB(pList))); + /* Unlink this list from the chain */ + if (pPrevList) + NET_BUFFER_LIST_NEXT_NBL(pPrevList) = pNextList; + else + pBufLists = pNextList; + NdisFreeNetBufferList(pList); +#ifdef VBOXNETLWF_SYNC_SEND + Log4(("vboxNetLwfWinReturnNetBufferLists: freed NBL+NB 0x%p\n", pList)); + KeSetEvent(&pModule->EventHost, 0, FALSE); +#else /* !VBOXNETLWF_SYNC_SEND */ + Log4(("vboxNetLwfWinReturnNetBufferLists: freed NBL+NB+MDL+Data 0x%p\n", pList)); + Assert(ASMAtomicReadS32(&pModule->cPendingBuffers) > 0); + if (ASMAtomicDecS32(&pModule->cPendingBuffers) == 0) + NdisSetEvent(&pModule->EventSendComplete); +#endif /* !VBOXNETLWF_SYNC_SEND */ + } + else + pPrevList = pList; + pList = pNextList; + } + if (pBufLists) + { + /* There are still lists remaining in the chain, pass'em up */ + NdisFReturnNetBufferLists(pModule->hFilter, pBufLists, fFlags); + } + LogFlow(("<==vboxNetLwfWinReturnNetBufferLists\n")); +} + +/** + * register the filter driver + */ +DECLHIDDEN(NDIS_STATUS) vboxNetLwfWinRegister(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegistryPathStr) +{ + RT_NOREF1(pRegistryPathStr); + NDIS_FILTER_DRIVER_CHARACTERISTICS FChars; + NDIS_STRING FriendlyName; + NDIS_STRING UniqueName; + NDIS_STRING ServiceName; + + NdisInitUnicodeString(&FriendlyName, VBOXNETLWF_NAME_FRIENDLY); + NdisInitUnicodeString(&UniqueName, VBOXNETLWF_NAME_UNIQUE); + NdisInitUnicodeString(&ServiceName, VBOXNETLWF_NAME_SERVICE); + + NdisZeroMemory(&FChars, sizeof (FChars)); + + FChars.Header.Type = NDIS_OBJECT_TYPE_FILTER_DRIVER_CHARACTERISTICS; + FChars.Header.Size = sizeof(NDIS_FILTER_DRIVER_CHARACTERISTICS); + FChars.Header.Revision = NDIS_FILTER_CHARACTERISTICS_REVISION_1; + + FChars.MajorNdisVersion = VBOXNETLWF_VERSION_NDIS_MAJOR; + FChars.MinorNdisVersion = VBOXNETLWF_VERSION_NDIS_MINOR; + + FChars.FriendlyName = FriendlyName; + FChars.UniqueName = UniqueName; + FChars.ServiceName = ServiceName; + + /* Mandatory functions */ + FChars.AttachHandler = vboxNetLwfWinAttach; + FChars.DetachHandler = vboxNetLwfWinDetach; + FChars.RestartHandler = vboxNetLwfWinRestart; + FChars.PauseHandler = vboxNetLwfWinPause; + + /* Optional functions, non changeble at run-time */ + FChars.OidRequestHandler = vboxNetLwfWinOidRequest; + FChars.OidRequestCompleteHandler = vboxNetLwfWinOidRequestComplete; + //FChars.CancelOidRequestHandler = vboxNetLwfWinCancelOidRequest; + FChars.StatusHandler = vboxNetLwfWinStatus; + //FChars.NetPnPEventHandler = vboxNetLwfWinPnPEvent; + + /* Datapath functions */ + FChars.SendNetBufferListsHandler = vboxNetLwfWinSendNetBufferLists; + FChars.SendNetBufferListsCompleteHandler = vboxNetLwfWinSendNetBufferListsComplete; + FChars.ReceiveNetBufferListsHandler = vboxNetLwfWinReceiveNetBufferLists; + FChars.ReturnNetBufferListsHandler = vboxNetLwfWinReturnNetBufferLists; + + pDriverObject->DriverUnload = vboxNetLwfWinUnloadDriver; + + NDIS_STATUS Status; + g_VBoxNetLwfGlobals.hFilterDriver = NULL; + Log(("vboxNetLwfWinRegister: registering filter driver...\n")); + Status = NdisFRegisterFilterDriver(pDriverObject, + (NDIS_HANDLE)&g_VBoxNetLwfGlobals, + &FChars, + &g_VBoxNetLwfGlobals.hFilterDriver); + Assert(Status == STATUS_SUCCESS); + if (Status == STATUS_SUCCESS) + { + Log(("vboxNetLwfWinRegister: successfully registered filter driver; registering device...\n")); + Status = vboxNetLwfWinDevCreate(&g_VBoxNetLwfGlobals); + Assert(Status == STATUS_SUCCESS); + Log(("vboxNetLwfWinRegister: vboxNetLwfWinDevCreate() returned 0x%x\n", Status)); + } + else + { + LogError(("vboxNetLwfWinRegister: failed to register filter driver, status=0x%x", Status)); + } + return Status; +} + +static int vboxNetLwfWinStartInitIdcThread() +{ + int rc = VERR_INVALID_STATE; + + if (ASMAtomicCmpXchgU32(&g_VBoxNetLwfGlobals.enmIdcState, LwfIdcState_Connecting, LwfIdcState_Disconnected)) + { + Log(("vboxNetLwfWinStartInitIdcThread: IDC state change Diconnected -> Connecting\n")); + + NTSTATUS Status = PsCreateSystemThread(&g_VBoxNetLwfGlobals.hInitIdcThread, + THREAD_ALL_ACCESS, + NULL, + NULL, + NULL, + vboxNetLwfWinInitIdcWorker, + &g_VBoxNetLwfGlobals); + Log(("vboxNetLwfWinStartInitIdcThread: create IDC initialization thread, status=0x%x\n", Status)); + if (Status != STATUS_SUCCESS) + { + LogError(("vboxNetLwfWinStartInitIdcThread: IDC initialization failed (system thread creation, status=0x%x)\n", Status)); + /* + * We failed to init IDC and there will be no second chance. + */ + Log(("vboxNetLwfWinStartInitIdcThread: IDC state change Connecting -> Diconnected\n")); + ASMAtomicWriteU32(&g_VBoxNetLwfGlobals.enmIdcState, LwfIdcState_Disconnected); + } + rc = RTErrConvertFromNtStatus(Status); + } + return rc; +} + +static void vboxNetLwfWinStopInitIdcThread() +{ +} + + +RT_C_DECLS_BEGIN + +NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING pRegistryPath); + +RT_C_DECLS_END + +NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING pRegistryPath) +{ + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + int rc; + + /* the idc registration is initiated via IOCTL since our driver + * can be loaded when the VBoxDrv is not in case we are a Ndis IM driver */ + rc = vboxNetLwfWinInitBase(); + AssertRC(rc); + if (RT_SUCCESS(rc)) + { + NdisZeroMemory(&g_VBoxNetLwfGlobals, sizeof (g_VBoxNetLwfGlobals)); + RTListInit(&g_VBoxNetLwfGlobals.listModules); + NdisAllocateSpinLock(&g_VBoxNetLwfGlobals.Lock); + /* + * We choose to ignore IDC initialization errors here because if we fail to load + * our filter the upper protocols won't bind to the associated adapter, causing + * network failure at the host. Better to have non-working filter than broken + * networking on the host. + */ + rc = vboxNetLwfWinStartInitIdcThread(); + AssertRC(rc); + + Status = vboxNetLwfWinRegister(pDriverObject, pRegistryPath); + Assert(Status == STATUS_SUCCESS); + if (Status == NDIS_STATUS_SUCCESS) + { + Log(("NETLWF: started successfully\n")); + return STATUS_SUCCESS; + } + NdisFreeSpinLock(&g_VBoxNetLwfGlobals.Lock); + vboxNetLwfWinFini(); + } + else + { + Status = NDIS_STATUS_FAILURE; + } + + return Status; +} + + +static VOID vboxNetLwfWinUnloadDriver(IN PDRIVER_OBJECT pDriver) +{ + RT_NOREF1(pDriver); + LogFlow(("==>vboxNetLwfWinUnloadDriver: driver=%p\n", pDriver)); + vboxNetLwfWinDevDestroy(&g_VBoxNetLwfGlobals); + NdisFDeregisterFilterDriver(g_VBoxNetLwfGlobals.hFilterDriver); + NdisFreeSpinLock(&g_VBoxNetLwfGlobals.Lock); + LogFlow(("<==vboxNetLwfWinUnloadDriver\n")); + vboxNetLwfWinFini(); +} + +static const char *vboxNetLwfWinIdcStateToText(uint32_t enmState) +{ + switch (enmState) + { + case LwfIdcState_Disconnected: return "Disconnected"; + case LwfIdcState_Connecting: return "Connecting"; + case LwfIdcState_Connected: return "Connected"; + case LwfIdcState_Stopping: return "Stopping"; + } + return "Unknown"; +} + +static VOID vboxNetLwfWinInitIdcWorker(PVOID pvContext) +{ + int rc; + PVBOXNETLWFGLOBALS pGlobals = (PVBOXNETLWFGLOBALS)pvContext; + + while (ASMAtomicReadU32(&pGlobals->enmIdcState) == LwfIdcState_Connecting) + { + rc = vboxNetFltInitIdc(&g_VBoxNetFltGlobals); + if (RT_SUCCESS(rc)) + { + if (!ASMAtomicCmpXchgU32(&pGlobals->enmIdcState, LwfIdcState_Connected, LwfIdcState_Connecting)) + { + /* The state has been changed (the only valid transition is to "Stopping"), undo init */ + rc = vboxNetFltTryDeleteIdc(&g_VBoxNetFltGlobals); + Log(("vboxNetLwfWinInitIdcWorker: state change (Connecting -> %s) while initializing IDC, deleted IDC, rc=0x%x\n", + vboxNetLwfWinIdcStateToText(ASMAtomicReadU32(&pGlobals->enmIdcState)), rc)); + } + else + { + Log(("vboxNetLwfWinInitIdcWorker: IDC state change Connecting -> Connected\n")); + } + } + else + { + LARGE_INTEGER WaitIn100nsUnits; + WaitIn100nsUnits.QuadPart = -(LONGLONG)10000000; /* 1 sec */ + KeDelayExecutionThread(KernelMode, FALSE /* non-alertable */, &WaitIn100nsUnits); + } + } + PsTerminateSystemThread(STATUS_SUCCESS); +} + +static int vboxNetLwfWinTryFiniIdc() +{ + int rc = VINF_SUCCESS; + NTSTATUS Status; + PKTHREAD pThread = NULL; + uint32_t enmPrevState = ASMAtomicXchgU32(&g_VBoxNetLwfGlobals.enmIdcState, LwfIdcState_Stopping); + + Log(("vboxNetLwfWinTryFiniIdc: IDC state change %s -> Stopping\n", vboxNetLwfWinIdcStateToText(enmPrevState))); + + switch (enmPrevState) + { + case LwfIdcState_Disconnected: + /* Have not even attempted to connect -- nothing to do. */ + break; + case LwfIdcState_Stopping: + /* Impossible, but another thread is alreading doing FiniIdc, bail out */ + LogError(("vboxNetLwfWinTryFiniIdc: called in 'Stopping' state\n")); + rc = VERR_INVALID_STATE; + break; + case LwfIdcState_Connecting: + /* the worker thread is running, let's wait for it to stop */ + Status = ObReferenceObjectByHandle(g_VBoxNetLwfGlobals.hInitIdcThread, + THREAD_ALL_ACCESS, NULL, KernelMode, + (PVOID*)&pThread, NULL); + if (Status == STATUS_SUCCESS) + { + KeWaitForSingleObject(pThread, Executive, KernelMode, FALSE, NULL); + ObDereferenceObject(pThread); + } + else + { + LogError(("vboxNetLwfWinTryFiniIdc: ObReferenceObjectByHandle(%p) failed with 0x%x\n", + g_VBoxNetLwfGlobals.hInitIdcThread, Status)); + } + rc = RTErrConvertFromNtStatus(Status); + break; + case LwfIdcState_Connected: + /* the worker succeeded in IDC init and terminated */ + rc = vboxNetFltTryDeleteIdc(&g_VBoxNetFltGlobals); + Log(("vboxNetLwfWinTryFiniIdc: deleted IDC, rc=0x%x\n", rc)); + break; + } + return rc; +} + +static void vboxNetLwfWinFiniBase() +{ + vboxNetFltDeleteGlobals(&g_VBoxNetFltGlobals); + + /* + * Undo the work done during start (in reverse order). + */ + memset(&g_VBoxNetFltGlobals, 0, sizeof(g_VBoxNetFltGlobals)); + + RTLogDestroy(RTLogRelSetDefaultInstance(NULL)); + RTLogDestroy(RTLogSetDefaultInstance(NULL)); + + RTR0Term(); +} + +static int vboxNetLwfWinInitBase() +{ + int rc = RTR0Init(0); + if (!RT_SUCCESS(rc)) + return rc; + + memset(&g_VBoxNetFltGlobals, 0, sizeof(g_VBoxNetFltGlobals)); + rc = vboxNetFltInitGlobals(&g_VBoxNetFltGlobals); + if (!RT_SUCCESS(rc)) + RTR0Term(); + + return rc; +} + +static int vboxNetLwfWinFini() +{ + int rc = vboxNetLwfWinTryFiniIdc(); + if (RT_SUCCESS(rc)) + { + vboxNetLwfWinFiniBase(); + } + return rc; +} + + +/* + * + * The OS specific interface definition + * + */ + + +bool vboxNetFltOsMaybeRediscovered(PVBOXNETFLTINS pThis) +{ + LogFlow(("==>vboxNetFltOsMaybeRediscovered: instance=%p\n", pThis)); + LogFlow(("<==vboxNetFltOsMaybeRediscovered: return %RTbool\n", !ASMAtomicUoReadBool(&pThis->fDisconnectedFromHost))); + /* AttachToInterface true if disconnected */ + return !ASMAtomicUoReadBool(&pThis->fDisconnectedFromHost); +} + +int vboxNetFltPortOsXmit(PVBOXNETFLTINS pThis, void *pvIfData, PINTNETSG pSG, uint32_t fDst) +{ + RT_NOREF1(pvIfData); + int rc = VINF_SUCCESS; + + PVBOXNETLWF_MODULE pModule = (PVBOXNETLWF_MODULE)pThis->u.s.WinIf.hModuleCtx; + LogFlow(("==>vboxNetFltPortOsXmit: instance=%p module=%p\n", pThis, pModule)); + if (!pModule) + { + LogFlow(("<==vboxNetFltPortOsXmit: pModule is null, return %d\n", VERR_INTERNAL_ERROR)); + return VERR_INTERNAL_ERROR; + } + /* Prevent going into "paused" state until all transmissions have been completed. */ + NDIS_WAIT_FOR_MUTEX(&pModule->InTransmit); + /* Ignore all sends if the stack is paused or being paused, etc... */ + if (!vboxNetLwfWinIsRunning(pModule)) + { + NDIS_RELEASE_MUTEX(&pModule->InTransmit); + return VINF_SUCCESS; + } + + vboxNetLwfWinDumpPacket(pSG, !(fDst & INTNETTRUNKDIR_WIRE) ? "intnet --> host" + : !(fDst & INTNETTRUNKDIR_HOST) ? "intnet --> wire" : "intnet --> all"); + + /* + * There are two possible strategies to deal with incoming SGs: + * 1) make a copy of data and complete asynchronously; + * 2) complete synchronously using the original data buffers. + * Before we consider implementing (1) it is quite interesting to see + * how well (2) performs. So we block until our requests are complete. + * Actually there is third possibility -- to use SG retain/release + * callbacks, but those seem not be fully implemented yet. + * Note that ansynchronous completion will require different implementation + * of vboxNetLwfWinPause(), not relying on InTransmit mutex. + */ +#ifdef VBOXNETLWF_SYNC_SEND + PVOID aEvents[2]; /* To wire and to host */ + ULONG nEvents = 0; + LARGE_INTEGER timeout; + timeout.QuadPart = -(LONGLONG)10000000; /* 1 sec */ +#endif /* VBOXNETLWF_SYNC_SEND */ + if (fDst & INTNETTRUNKDIR_WIRE) + { + PNET_BUFFER_LIST pBufList = vboxNetLwfWinSGtoNB(pModule, pSG); + if (pBufList) + { + vboxNetLwfWinDumpPackets("vboxNetFltPortOsXmit: sending down", pBufList); +#ifdef VBOXNETLWF_SYNC_SEND + aEvents[nEvents++] = &pModule->EventWire; +#else /* !VBOXNETLWF_SYNC_SEND */ + if (ASMAtomicIncS32(&pModule->cPendingBuffers) == 1) + NdisResetEvent(&pModule->EventSendComplete); +#endif /* !VBOXNETLWF_SYNC_SEND */ + NdisFSendNetBufferLists(pModule->hFilter, pBufList, NDIS_DEFAULT_PORT_NUMBER, 0); /** @todo sendFlags! */ + } + } + if (fDst & INTNETTRUNKDIR_HOST) + { + PNET_BUFFER_LIST pBufList = vboxNetLwfWinSGtoNB(pModule, pSG); + if (pBufList) + { + vboxNetLwfWinDumpPackets("vboxNetFltPortOsXmit: sending up", pBufList); +#ifdef VBOXNETLWF_SYNC_SEND + aEvents[nEvents++] = &pModule->EventHost; +#else /* !VBOXNETLWF_SYNC_SEND */ + if (ASMAtomicIncS32(&pModule->cPendingBuffers) == 1) + NdisResetEvent(&pModule->EventSendComplete); +#endif /* !VBOXNETLWF_SYNC_SEND */ + NdisFIndicateReceiveNetBufferLists(pModule->hFilter, pBufList, NDIS_DEFAULT_PORT_NUMBER, 1, 0); + } + } +#ifdef VBOXNETLWF_SYNC_SEND + if (nEvents) + { + NTSTATUS Status = KeWaitForMultipleObjects(nEvents, aEvents, WaitAll, Executive, KernelMode, FALSE, &timeout, NULL); + if (Status != STATUS_SUCCESS) + { + LogError(("vboxNetFltPortOsXmit: KeWaitForMultipleObjects() failed with 0x%x\n", Status)); + if (Status == STATUS_TIMEOUT) + rc = VERR_TIMEOUT; + else + rc = RTErrConvertFromNtStatus(Status); + } + } +#endif /* VBOXNETLWF_SYNC_SEND */ + NDIS_RELEASE_MUTEX(&pModule->InTransmit); + + LogFlow(("<==vboxNetFltPortOsXmit: return %d\n", rc)); + return rc; +} + + +NDIS_IO_WORKITEM_FUNCTION vboxNetLwfWinToggleOffloading; + +VOID vboxNetLwfWinToggleOffloading(PVOID WorkItemContext, NDIS_HANDLE NdisIoWorkItemHandle) +{ + /* WARNING! Call this with IRQL=Passive! */ + RT_NOREF1(NdisIoWorkItemHandle); + PVBOXNETLWF_MODULE pModuleCtx = (PVBOXNETLWF_MODULE)WorkItemContext; + + if (ASMAtomicReadBool(&pModuleCtx->fActive)) + { + /* Disable offloading temporarily by indicating offload config change. */ + /** @todo Be sure to revise this when implementing offloading support! */ + vboxNetLwfWinIndicateOffload(pModuleCtx, pModuleCtx->pDisabledOffloadConfig); + Log(("vboxNetLwfWinToggleOffloading: set offloading off\n")); + } + else + { + /* The filter is inactive -- restore offloading configuration. */ + if (pModuleCtx->fOffloadConfigValid) + { + vboxNetLwfWinIndicateOffload(pModuleCtx, pModuleCtx->pSavedOffloadConfig); + Log(("vboxNetLwfWinToggleOffloading: restored offloading config\n")); + } + else + DbgPrint("VBoxNetLwf: no saved offload config to restore for %s\n", pModuleCtx->szMiniportName); + } +} + + +void vboxNetFltPortOsSetActive(PVBOXNETFLTINS pThis, bool fActive) +{ + PVBOXNETLWF_MODULE pModuleCtx = (PVBOXNETLWF_MODULE)pThis->u.s.WinIf.hModuleCtx; + LogFlow(("==>vboxNetFltPortOsSetActive: instance=%p module=%p fActive=%RTbool\n", pThis, pModuleCtx, fActive)); + if (!pModuleCtx) + { + LogFlow(("<==vboxNetFltPortOsSetActive: pModuleCtx is null\n")); + return; + } + + NDIS_STATUS Status = STATUS_SUCCESS; + bool fOldActive = ASMAtomicXchgBool(&pModuleCtx->fActive, fActive); + if (fOldActive != fActive) + { + NdisQueueIoWorkItem(pModuleCtx->hWorkItem, vboxNetLwfWinToggleOffloading, pModuleCtx); + Status = vboxNetLwfWinSetPacketFilter(pModuleCtx, fActive); + LogFlow(("<==vboxNetFltPortOsSetActive: vboxNetLwfWinSetPacketFilter() returned 0x%x\n", Status)); + } + else + LogFlow(("<==vboxNetFltPortOsSetActive: no change, remain %sactive\n", fActive ? "":"in")); +} + +int vboxNetFltOsDisconnectIt(PVBOXNETFLTINS pThis) +{ + RT_NOREF1(pThis); + LogFlow(("==>vboxNetFltOsDisconnectIt: instance=%p\n", pThis)); + LogFlow(("<==vboxNetFltOsDisconnectIt: return 0\n")); + return VINF_SUCCESS; +} + +int vboxNetFltOsConnectIt(PVBOXNETFLTINS pThis) +{ + RT_NOREF1(pThis); + LogFlow(("==>vboxNetFltOsConnectIt: instance=%p\n", pThis)); + LogFlow(("<==vboxNetFltOsConnectIt: return 0\n")); + return VINF_SUCCESS; +} + +/* + * Uncommenting the following line produces debug log messages on IP address changes, + * including wired interfaces. No actual calls to a switch port are made. This is for + * debug purposes only! + * #define VBOXNETLWFWIN_DEBUGIPADDRNOTIF 1 + */ +static void __stdcall vboxNetLwfWinIpAddrChangeCallback(IN PVOID pvCtx, + IN PMIB_UNICASTIPADDRESS_ROW pRow, + IN MIB_NOTIFICATION_TYPE enmNotifType) +{ + PVBOXNETFLTINS pThis = (PVBOXNETFLTINS)pvCtx; + + /* We are only interested in add or remove notifications. */ + bool fAdded; + if (enmNotifType == MibAddInstance) + fAdded = true; + else if (enmNotifType == MibDeleteInstance) + fAdded = false; + else + return; + + if ( pRow +#ifndef VBOXNETLWFWIN_DEBUGIPADDRNOTIF + && pThis->pSwitchPort->pfnNotifyHostAddress +#endif /* !VBOXNETLWFWIN_DEBUGIPADDRNOTIF */ + ) + { + switch (pRow->Address.si_family) + { + case AF_INET: + if ( IN4_IS_ADDR_LINKLOCAL(&pRow->Address.Ipv4.sin_addr) + || pRow->Address.Ipv4.sin_addr.s_addr == IN4ADDR_LOOPBACK) + { + Log(("vboxNetLwfWinIpAddrChangeCallback: ignoring %s address (%RTnaipv4)\n", + pRow->Address.Ipv4.sin_addr.s_addr == IN4ADDR_LOOPBACK ? "loopback" : "link-local", + pRow->Address.Ipv4.sin_addr)); + break; + } + Log(("vboxNetLwfWinIpAddrChangeCallback: %s IPv4 addr=%RTnaipv4 on luid=(%u,%u)\n", + fAdded ? "add" : "remove", pRow->Address.Ipv4.sin_addr, + pRow->InterfaceLuid.Info.IfType, pRow->InterfaceLuid.Info.NetLuidIndex)); +#ifndef VBOXNETLWFWIN_DEBUGIPADDRNOTIF + pThis->pSwitchPort->pfnNotifyHostAddress(pThis->pSwitchPort, fAdded, kIntNetAddrType_IPv4, + &pRow->Address.Ipv4.sin_addr); +#endif /* !VBOXNETLWFWIN_DEBUGIPADDRNOTIF */ + break; + case AF_INET6: + if (Ipv6AddressScope(pRow->Address.Ipv6.sin6_addr.u.Byte) <= ScopeLevelLink) + { + Log(("vboxNetLwfWinIpAddrChangeCallback: ignoring link-local address (%RTnaipv6)\n", + &pRow->Address.Ipv6.sin6_addr)); + break; + } + Log(("vboxNetLwfWinIpAddrChangeCallback: %s IPv6 addr=%RTnaipv6 scope=%d luid=(%u,%u)\n", + fAdded ? "add" : "remove", &pRow->Address.Ipv6.sin6_addr, + Ipv6AddressScope(pRow->Address.Ipv6.sin6_addr.u.Byte), + pRow->InterfaceLuid.Info.IfType, pRow->InterfaceLuid.Info.NetLuidIndex)); +#ifndef VBOXNETLWFWIN_DEBUGIPADDRNOTIF + pThis->pSwitchPort->pfnNotifyHostAddress(pThis->pSwitchPort, fAdded, kIntNetAddrType_IPv6, + &pRow->Address.Ipv6.sin6_addr); +#endif /* !VBOXNETLWFWIN_DEBUGIPADDRNOTIF */ + break; + } + } + else + Log(("vboxNetLwfWinIpAddrChangeCallback: pRow=%p pfnNotifyHostAddress=%p\n", + pRow, pThis->pSwitchPort->pfnNotifyHostAddress)); +} + +void vboxNetLwfWinRegisterIpAddrNotifier(PVBOXNETFLTINS pThis) +{ + LogFlow(("==>vboxNetLwfWinRegisterIpAddrNotifier: instance=%p\n", pThis)); + if ( pThis->pSwitchPort +#ifndef VBOXNETLWFWIN_DEBUGIPADDRNOTIF + && pThis->pSwitchPort->pfnNotifyHostAddress +#endif /* !VBOXNETLWFWIN_DEBUGIPADDRNOTIF */ + ) + { + NETIO_STATUS Status; + /* First we need to go over all host IP addresses and add them via pfnNotifyHostAddress. */ + PMIB_UNICASTIPADDRESS_TABLE HostIpAddresses = NULL; + Status = GetUnicastIpAddressTable(AF_UNSPEC, &HostIpAddresses); + if (NETIO_SUCCESS(Status)) + { + for (unsigned i = 0; i < HostIpAddresses->NumEntries; i++) + vboxNetLwfWinIpAddrChangeCallback(pThis, &HostIpAddresses->Table[i], MibAddInstance); + } + else + LogError(("vboxNetLwfWinRegisterIpAddrNotifier: GetUnicastIpAddressTable failed with %x\n", Status)); + /* Now we can register a callback function to keep track of address changes. */ + Status = NotifyUnicastIpAddressChange(AF_UNSPEC, vboxNetLwfWinIpAddrChangeCallback, + pThis, false, &pThis->u.s.WinIf.hNotifier); + if (NETIO_SUCCESS(Status)) + Log(("vboxNetLwfWinRegisterIpAddrNotifier: notifier=%p\n", pThis->u.s.WinIf.hNotifier)); + else + LogError(("vboxNetLwfWinRegisterIpAddrNotifier: NotifyUnicastIpAddressChange failed with %x\n", Status)); + } + else + pThis->u.s.WinIf.hNotifier = NULL; + LogFlow(("<==vboxNetLwfWinRegisterIpAddrNotifier\n")); +} + +void vboxNetLwfWinUnregisterIpAddrNotifier(PVBOXNETFLTINS pThis) +{ + Log(("vboxNetLwfWinUnregisterIpAddrNotifier: notifier=%p\n", pThis->u.s.WinIf.hNotifier)); + if (pThis->u.s.WinIf.hNotifier) + CancelMibChangeNotify2(pThis->u.s.WinIf.hNotifier); +} + +void vboxNetFltOsDeleteInstance(PVBOXNETFLTINS pThis) +{ + PVBOXNETLWF_MODULE pModuleCtx = (PVBOXNETLWF_MODULE)pThis->u.s.WinIf.hModuleCtx; + LogFlow(("==>vboxNetFltOsDeleteInstance: instance=%p module=%p\n", pThis, pModuleCtx)); + /* Cancel IP address change notifications */ + vboxNetLwfWinUnregisterIpAddrNotifier(pThis); + /* Technically it is possible that the module has already been gone by now. */ + if (pModuleCtx) + { + Assert(!pModuleCtx->fActive); /* Deactivation ensures bypass mode */ + pModuleCtx->pNetFlt = NULL; + pThis->u.s.WinIf.hModuleCtx = NULL; + } + LogFlow(("<==vboxNetFltOsDeleteInstance\n")); +} + +static void vboxNetLwfWinReportCapabilities(PVBOXNETFLTINS pThis, PVBOXNETLWF_MODULE pModuleCtx) +{ + if (pThis->pSwitchPort + && vboxNetFltTryRetainBusyNotDisconnected(pThis)) + { + pThis->pSwitchPort->pfnReportMacAddress(pThis->pSwitchPort, &pModuleCtx->MacAddr); + pThis->pSwitchPort->pfnReportPromiscuousMode(pThis->pSwitchPort, + vboxNetLwfWinIsPromiscuous(pModuleCtx)); + pThis->pSwitchPort->pfnReportGsoCapabilities(pThis->pSwitchPort, 0, + INTNETTRUNKDIR_WIRE | INTNETTRUNKDIR_HOST); + pThis->pSwitchPort->pfnReportNoPreemptDsts(pThis->pSwitchPort, 0 /* none */); + vboxNetFltRelease(pThis, true /*fBusy*/); + } +} + +int vboxNetFltOsInitInstance(PVBOXNETFLTINS pThis, void *pvContext) +{ + RT_NOREF1(pvContext); + LogFlow(("==>vboxNetFltOsInitInstance: instance=%p context=%p\n", pThis, pvContext)); + AssertReturn(pThis, VERR_INVALID_PARAMETER); + Log(("vboxNetFltOsInitInstance: trunk name=%s\n", pThis->szName)); + NdisAcquireSpinLock(&g_VBoxNetLwfGlobals.Lock); + PVBOXNETLWF_MODULE pModuleCtx; + RTListForEach(&g_VBoxNetLwfGlobals.listModules, pModuleCtx, VBOXNETLWF_MODULE, node) + { + DbgPrint("vboxNetFltOsInitInstance: evaluating module, name=%s\n", pModuleCtx->szMiniportName); + if (!RTStrICmp(pThis->szName, pModuleCtx->szMiniportName)) + { + NdisReleaseSpinLock(&g_VBoxNetLwfGlobals.Lock); + Log(("vboxNetFltOsInitInstance: found matching module, name=%s\n", pThis->szName)); + pThis->u.s.WinIf.hModuleCtx = pModuleCtx; + pModuleCtx->pNetFlt = pThis; + vboxNetLwfWinReportCapabilities(pThis, pModuleCtx); + vboxNetLwfWinRegisterIpAddrNotifier(pThis); + LogFlow(("<==vboxNetFltOsInitInstance: return 0\n")); + return VINF_SUCCESS; + } + } + NdisReleaseSpinLock(&g_VBoxNetLwfGlobals.Lock); + // Internal network code will try to reconnect periodically, we should not spam in event log + //vboxNetLwfLogErrorEvent(IO_ERR_INTERNAL_ERROR, STATUS_SUCCESS, 6); + LogFlow(("<==vboxNetFltOsInitInstance: return VERR_INTNET_FLT_IF_NOT_FOUND\n")); + return VERR_INTNET_FLT_IF_NOT_FOUND; +} + +int vboxNetFltOsPreInitInstance(PVBOXNETFLTINS pThis) +{ + LogFlow(("==>vboxNetFltOsPreInitInstance: instance=%p\n", pThis)); + pThis->u.s.WinIf.hModuleCtx = 0; + pThis->u.s.WinIf.hNotifier = NULL; + LogFlow(("<==vboxNetFltOsPreInitInstance: return 0\n")); + return VINF_SUCCESS; +} + +void vboxNetFltPortOsNotifyMacAddress(PVBOXNETFLTINS pThis, void *pvIfData, PCRTMAC pMac) +{ + RT_NOREF3(pThis, pvIfData, pMac); + LogFlow(("==>vboxNetFltPortOsNotifyMacAddress: instance=%p data=%p mac=%RTmac\n", pThis, pvIfData, pMac)); + LogFlow(("<==vboxNetFltPortOsNotifyMacAddress\n")); +} + +int vboxNetFltPortOsConnectInterface(PVBOXNETFLTINS pThis, void *pvIf, void **ppvIfData) +{ + RT_NOREF3(pThis, pvIf, ppvIfData); + LogFlow(("==>vboxNetFltPortOsConnectInterface: instance=%p if=%p data=%p\n", pThis, pvIf, ppvIfData)); + LogFlow(("<==vboxNetFltPortOsConnectInterface: return 0\n")); + /* Nothing to do */ + return VINF_SUCCESS; +} + +int vboxNetFltPortOsDisconnectInterface(PVBOXNETFLTINS pThis, void *pvIfData) +{ + RT_NOREF2(pThis, pvIfData); + LogFlow(("==>vboxNetFltPortOsDisconnectInterface: instance=%p data=%p\n", pThis, pvIfData)); + LogFlow(("<==vboxNetFltPortOsDisconnectInterface: return 0\n")); + /* Nothing to do */ + return VINF_SUCCESS; +} + diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetLwf-win.h b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetLwf-win.h new file mode 100644 index 00000000..d0bd58e5 --- /dev/null +++ b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetLwf-win.h @@ -0,0 +1,45 @@ +/* $Id: VBoxNetLwf-win.h $ */ +/** @file + * VBoxNetLwf-win.h - Bridged Networking Driver, Windows-specific code. + */ +/* + * Copyright (C) 2014-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * 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. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE 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. + */ + +#ifndef VBOX_INCLUDED_SRC_VBoxNetFlt_win_drv_VBoxNetLwf_win_h +#define VBOX_INCLUDED_SRC_VBoxNetFlt_win_drv_VBoxNetLwf_win_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#define VBOXNETLWF_VERSION_NDIS_MAJOR 6 +#define VBOXNETLWF_VERSION_NDIS_MINOR 0 + +#define VBOXNETLWF_NAME_FRIENDLY L"VirtualBox NDIS Light-Weight Filter" +#define VBOXNETLWF_NAME_UNIQUE L"{7af6b074-048d-4444-bfce-1ecc8bc5cb76}" +#define VBOXNETLWF_NAME_SERVICE L"VBoxNetLwf" + +#define VBOXNETLWF_NAME_LINK L"\\DosDevices\\Global\\VBoxNetLwf" +#define VBOXNETLWF_NAME_DEVICE L"\\Device\\VBoxNetLwf" + +#define VBOXNETLWF_MEM_TAG 'FLBV' +#define VBOXNETLWF_REQ_ID 'fLBV' + +#endif /* !VBOX_INCLUDED_SRC_VBoxNetFlt_win_drv_VBoxNetLwf_win_h */ diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetLwf.inf b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetLwf.inf new file mode 100644 index 00000000..eda00f56 --- /dev/null +++ b/src/VBox/HostDrivers/VBoxNetFlt/win/drv/VBoxNetLwf.inf @@ -0,0 +1,116 @@ +; $Id: VBoxNetLwf.inf $ +; @file +; VBoxNetLwf.inf - VirtualBox Bridged Networking Driver inf file +; + +; +; Copyright (C) 2014-2019 Oracle Corporation +; +; This file is part of VirtualBox Open Source Edition (OSE), as +; available from http://www.virtualbox.org. This file is free software; +; you can redistribute it and/or modify it under the terms of the GNU +; 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. +; +; The contents of this file may alternatively be used under the terms +; of the Common Development and Distribution License Version 1.0 +; (CDDL) only, as it comes in the "COPYING.CDDL" file of the +; VirtualBox OSE 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. +; + +[Version] +Signature = "$Windows NT$" +;cat CatalogFile = VBoxNetLwf.cat +Class = NetService +ClassGUID = {4D36E974-E325-11CE-BFC1-08002BE10318} +Provider = %Provider% +;edit-DriverVer=10/23/2014,1.0.1.0 + + +[Manufacturer] +%Provider% = VBox,NTx86,NTamd64 + +[ControlFlags] + +[VBox] +%VBoxNetLwf_Desc% = VBoxNetLwf.ndi, oracle_VBoxNetLwf + +[VBox.NTx86] +%VBoxNetLwf_Desc% = VBoxNetLwf.ndi, oracle_VBoxNetLwf + +[VBox.NTamd64] +%VBoxNetLwf_Desc% = VBoxNetLwf.ndi, oracle_VBoxNetLwf + +[VBoxNetLwf.ndi] +AddReg = VBoxNetLwf.ndi.AddReg, VBoxNetLwf.AddReg +Characteristics = 0x40000 ; NCF_LW_FILTER +CopyFiles = VBoxNetLwf.Files.Sys +NetCfgInstanceId = "{7af6b074-048d-4444-bfce-1ecc8bc5cb76}" + +[VBoxNetLwf.ndi.Remove.Services] +DelService = VBoxNetLwf,0x200 ; Stop the service before uninstalling + +[VBoxNetLwf.ndi.Services] +AddService = VBoxNetLwf,, VBoxNetLwf.AddService, VBoxNetLwf.AddEventLog + +[VBoxNetLwf.AddService] +DisplayName = %VBoxNetLwfService_Desc% +ServiceType = 1 ;SERVICE_KERNEL_DRIVER +StartType = 1 ;SERVICE_SYSTEM_START +ErrorControl = 1 ;SERVICE_ERROR_NORMAL +ServiceBinary = %12%\VBoxNetLwf.sys +LoadOrderGroup = NDIS +AddReg = VBoxNetLwf.AddService.AddReg + +[VBoxNetLwf.AddService.AddReg] + +[VBoxNetLwf.AddEventLog] +AddReg = VBoxNetLwf.AddEventLog.AddReg + +[VBoxNetLwf.AddEventLog.AddReg] +HKR,,EventMessageFile,0x00020000,"%%SystemRoot%%\System32\IoLogMsg.dll" +HKR,,TypesSupported,0x00010001,7 + + +[SourceDisksNames] +1=%DiskDescription%,"",, + +[SourceDisksFiles] +VBoxNetLwf.sys=1 + +[DestinationDirs] +DefaultDestDir = 12 +VBoxNetLwf.Files.Sys = 12 ; %windir%\System32\drivers + +[VBoxNetLwf.Files.Sys] +VBoxNetLwf.sys,,,2 + + +[VBoxNetLwf.ndi.AddReg] +HKR, Ndi, HelpText, , %VBoxNetLwf_HELP% +;HKR, Ndi, ClsID, 0, {f374d1a0-bf08-4bdc-9cb2-c15ddaeef955} +;HKR, Ndi, ComponentDll, , VBoxNetLwfNobj.dll +HKR, Ndi, FilterClass, , compression +HKR, Ndi, FilterType, 0x10001, 0x2 +HKR, Ndi, FilterRunType,0x10001, 2 ; OPTIONAL, to prevent unbinding of protocol drivers +HKR, Ndi, Service, , VBoxNetLwf +HKR, Ndi, CoServices, 0x10000, VBoxNetLwf +HKR, Ndi\Interfaces, UpperRange, , noupper +HKR, Ndi\Interfaces, LowerRange, , nolower +HKR, Ndi\Interfaces, FilterMediaTypes, , ethernet + +[VBoxNetLwf.AddReg] +;HKR, Parameters, Param1, 0, 4 + +[Strings] +Provider = "Oracle Corporation" +DiskDescription = "VirtualBox NDIS6 Bridged Networking Driver" +VBoxNetLwf_Desc = "VirtualBox NDIS6 Bridged Networking Driver" +VBoxNetLwf_HELP = "VirtualBox NDIS6 Bridged Networking Driver" +VBoxNetLwfService_Desc = "VirtualBox NDIS6 Bridged Networking Service" diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/Makefile.kup b/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/Makefile.kup new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/Makefile.kup diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobj.cpp b/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobj.cpp new file mode 100644 index 00000000..67c667f4 --- /dev/null +++ b/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobj.cpp @@ -0,0 +1,746 @@ +/* $Id: VBoxNetFltNobj.cpp $ */ +/** @file + * VBoxNetFltNobj.cpp - Notify Object for Bridged Networking Driver. + * Used to filter Bridged Networking Driver bindings + */ +/* + * Copyright (C) 2011-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * 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. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE 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. + */ +#include "VBoxNetFltNobj.h" +#include <iprt/win/ntddndis.h> +#include <assert.h> +#include <stdio.h> + +#include <VBoxNetFltNobjT_i.c> + +#include <Olectl.h> + +//# define VBOXNETFLTNOTIFY_DEBUG_BIND + +#ifdef DEBUG +# define NonStandardAssert(a) assert(a) +# define NonStandardAssertBreakpoint() assert(0) +#else +# define NonStandardAssert(a) do{}while (0) +# define NonStandardAssertBreakpoint() do{}while (0) +#endif + +VBoxNetFltNobj::VBoxNetFltNobj() : + mpNetCfg(NULL), + mpNetCfgComponent(NULL), + mbInstalling(FALSE) +{ +} + +VBoxNetFltNobj::~VBoxNetFltNobj() +{ + cleanup(); +} + +void VBoxNetFltNobj::cleanup() +{ + if (mpNetCfg) + { + mpNetCfg->Release(); + mpNetCfg = NULL; + } + + if (mpNetCfgComponent) + { + mpNetCfgComponent->Release(); + mpNetCfgComponent = NULL; + } +} + +void VBoxNetFltNobj::init(IN INetCfgComponent *pNetCfgComponent, IN INetCfg *pNetCfg, IN BOOL bInstalling) +{ + cleanup(); + + NonStandardAssert(pNetCfg); + NonStandardAssert(pNetCfgComponent); + if (pNetCfg) + { + pNetCfg->AddRef(); + mpNetCfg = pNetCfg; + } + + if (pNetCfgComponent) + { + pNetCfgComponent->AddRef(); + mpNetCfgComponent = pNetCfgComponent; + } + + mbInstalling = bInstalling; +} + +/* INetCfgComponentControl methods */ +STDMETHODIMP VBoxNetFltNobj::Initialize(IN INetCfgComponent *pNetCfgComponent, IN INetCfg *pNetCfg, IN BOOL bInstalling) +{ + init(pNetCfgComponent, pNetCfg, bInstalling); + return S_OK; +} + +STDMETHODIMP VBoxNetFltNobj::ApplyRegistryChanges() +{ + return S_OK; +} + +STDMETHODIMP VBoxNetFltNobj::ApplyPnpChanges(IN INetCfgPnpReconfigCallback *pCallback) +{ + RT_NOREF1(pCallback); + return S_OK; +} + +STDMETHODIMP VBoxNetFltNobj::CancelChanges() +{ + return S_OK; +} + +static HRESULT vboxNetFltWinQueryInstanceKey(IN INetCfgComponent *pComponent, OUT PHKEY phKey) +{ + LPWSTR pPnpId; + HRESULT hr = pComponent->GetPnpDevNodeId(&pPnpId); + if (hr == S_OK) + { + WCHAR KeyName[MAX_PATH]; + wcscpy(KeyName, L"SYSTEM\\CurrentControlSet\\Enum\\"); + wcscat(KeyName,pPnpId); + + LONG winEr = RegOpenKeyExW(HKEY_LOCAL_MACHINE, KeyName, + 0, /*__reserved DWORD ulOptions*/ + KEY_READ, /*__in REGSAM samDesired*/ + phKey); + + if (winEr != ERROR_SUCCESS) + { + hr = HRESULT_FROM_WIN32(winEr); + NonStandardAssertBreakpoint(); + } + + CoTaskMemFree(pPnpId); + } + else + { + NonStandardAssertBreakpoint(); + } + + return hr; +} + +static HRESULT vboxNetFltWinQueryDriverKey(IN HKEY InstanceKey, OUT PHKEY phKey) +{ + DWORD Type = REG_SZ; + WCHAR Value[MAX_PATH]; + DWORD cbValue = sizeof(Value); + HRESULT hr = S_OK; + LONG winEr = RegQueryValueExW(InstanceKey, + L"Driver", /*__in_opt LPCTSTR lpValueName*/ + 0, /*__reserved LPDWORD lpReserved*/ + &Type, /*__out_opt LPDWORD lpType*/ + (LPBYTE)Value, /*__out_opt LPBYTE lpData*/ + &cbValue/*__inout_opt LPDWORD lpcbData*/ + ); + + if (winEr == ERROR_SUCCESS) + { + WCHAR KeyName[MAX_PATH]; + wcscpy(KeyName, L"SYSTEM\\CurrentControlSet\\Control\\Class\\"); + wcscat(KeyName,Value); + + winEr = RegOpenKeyExW(HKEY_LOCAL_MACHINE, KeyName, + 0, /*__reserved DWORD ulOptions*/ + KEY_READ, /*__in REGSAM samDesired*/ + phKey); + + if (winEr != ERROR_SUCCESS) + { + hr = HRESULT_FROM_WIN32(winEr); + NonStandardAssertBreakpoint(); + } + } + else + { + hr = HRESULT_FROM_WIN32(winEr); + NonStandardAssertBreakpoint(); + } + + return hr; +} + +static HRESULT vboxNetFltWinQueryDriverKey(IN INetCfgComponent *pComponent, OUT PHKEY phKey) +{ + HKEY InstanceKey; + HRESULT hr = vboxNetFltWinQueryInstanceKey(pComponent, &InstanceKey); + if (hr == S_OK) + { + hr = vboxNetFltWinQueryDriverKey(InstanceKey, phKey); + if (hr != S_OK) + { + NonStandardAssertBreakpoint(); + } + RegCloseKey(InstanceKey); + } + else + { + NonStandardAssertBreakpoint(); + } + + return hr; +} + +static HRESULT vboxNetFltWinNotifyCheckNetAdp(IN INetCfgComponent *pComponent, OUT bool * pbShouldBind) +{ + HRESULT hr; + LPWSTR pDevId; + hr = pComponent->GetId(&pDevId); + if (hr == S_OK) + { + if (!_wcsnicmp(pDevId, L"sun_VBoxNetAdp", sizeof(L"sun_VBoxNetAdp")/2)) + { + *pbShouldBind = false; + } + else + { + hr = S_FALSE; + } + CoTaskMemFree(pDevId); + } + else + { + NonStandardAssertBreakpoint(); + } + + return hr; +} + +static HRESULT vboxNetFltWinNotifyCheckMsLoop(IN INetCfgComponent *pComponent, OUT bool * pbShouldBind) +{ + HRESULT hr; + LPWSTR pDevId; + hr = pComponent->GetId(&pDevId); + if (hr == S_OK) + { + if (!_wcsnicmp(pDevId, L"*msloop", sizeof(L"*msloop")/2)) + { + /* we need to detect the medium the adapter is presenting + * to do that we could examine in the registry the *msloop params */ + HKEY DriverKey; + hr = vboxNetFltWinQueryDriverKey(pComponent, &DriverKey); + if (hr == S_OK) + { + DWORD Type = REG_SZ; + WCHAR Value[64]; /* 2 should be enough actually, paranoid check for extra spaces */ + DWORD cbValue = sizeof(Value); + LONG winEr = RegQueryValueExW(DriverKey, + L"Medium", /*__in_opt LPCTSTR lpValueName*/ + 0, /*__reserved LPDWORD lpReserved*/ + &Type, /*__out_opt LPDWORD lpType*/ + (LPBYTE)Value, /*__out_opt LPBYTE lpData*/ + &cbValue/*__inout_opt LPDWORD lpcbData*/ + ); + if (winEr == ERROR_SUCCESS) + { + PWCHAR endPrt; + ULONG enmMedium = wcstoul(Value, + &endPrt, + 0 /* base*/); + + winEr = errno; + if (winEr == ERROR_SUCCESS) + { + if (enmMedium == 0) /* 0 is Ethernet */ + { + *pbShouldBind = true; + } + else + { + *pbShouldBind = false; + } + } + else + { + NonStandardAssertBreakpoint(); + *pbShouldBind = true; + } + } + else + { + /** @todo we should check the default medium in HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002bE10318}\<driver_id>\Ndi\Params\Medium, REG_SZ "Default" value */ + NonStandardAssertBreakpoint(); + *pbShouldBind = true; + } + + RegCloseKey(DriverKey); + } + else + { + NonStandardAssertBreakpoint(); + } + } + else + { + hr = S_FALSE; + } + CoTaskMemFree(pDevId); + } + else + { + NonStandardAssertBreakpoint(); + } + + return hr; +} + +static HRESULT vboxNetFltWinNotifyCheckLowerRange(IN INetCfgComponent *pComponent, OUT bool * pbShouldBind) +{ + HKEY DriverKey; + HKEY InterfacesKey; + HRESULT hr = vboxNetFltWinQueryDriverKey(pComponent, &DriverKey); + if (hr == S_OK) + { + LONG winEr = RegOpenKeyExW(DriverKey, L"Ndi\\Interfaces", + 0, /*__reserved DWORD ulOptions*/ + KEY_READ, /*__in REGSAM samDesired*/ + &InterfacesKey); + if (winEr == ERROR_SUCCESS) + { + DWORD Type = REG_SZ; + WCHAR Value[MAX_PATH]; + DWORD cbValue = sizeof(Value); + winEr = RegQueryValueExW(InterfacesKey, + L"LowerRange", /*__in_opt LPCTSTR lpValueName*/ + 0, /*__reserved LPDWORD lpReserved*/ + &Type, /*__out_opt LPDWORD lpType*/ + (LPBYTE)Value, /*__out_opt LPBYTE lpData*/ + &cbValue/*__inout_opt LPDWORD lpcbData*/ + ); + if (winEr == ERROR_SUCCESS) + { + if (wcsstr(Value,L"ethernet") || wcsstr(Value, L"wan")) + { + *pbShouldBind = true; + } + else + { + *pbShouldBind = false; + } + } + else + { + /* do not set err status to it */ + *pbShouldBind = false; + NonStandardAssertBreakpoint(); + } + + RegCloseKey(InterfacesKey); + } + else + { + hr = HRESULT_FROM_WIN32(winEr); + NonStandardAssertBreakpoint(); + } + + RegCloseKey(DriverKey); + } + else + { + NonStandardAssertBreakpoint(); + } + + return hr; +} + +static HRESULT vboxNetFltWinNotifyShouldBind(IN INetCfgComponent *pComponent, OUT bool *pbShouldBind) +{ + DWORD fCharacteristics; + HRESULT hr; + + do + { + /* filter out only physical adapters */ + hr = pComponent->GetCharacteristics(&fCharacteristics); + if (hr != S_OK) + { + NonStandardAssertBreakpoint(); + break; + } + + + if (fCharacteristics & NCF_HIDDEN) + { + /* we are not binding to hidden adapters */ + *pbShouldBind = false; + break; + } + + hr = vboxNetFltWinNotifyCheckMsLoop(pComponent, pbShouldBind); + if (hr == S_OK) + { + /* this is a loopback adapter, + * the pbShouldBind already contains the result */ + break; + } + else if (hr != S_FALSE) + { + /* error occurred */ + break; + } + + hr = vboxNetFltWinNotifyCheckNetAdp(pComponent, pbShouldBind); + if (hr == S_OK) + { + /* this is a VBoxNetAdp adapter, + * the pbShouldBind already contains the result */ + break; + } + else if (hr != S_FALSE) + { + /* error occurred */ + break; + } + + /* hr == S_FALSE means this is not a loopback adpater, set it to S_OK */ + hr = S_OK; + +// if (!(fCharacteristics & NCF_PHYSICAL)) +// { +// /* we are binding to physical adapters only */ +// *pbShouldBind = false; +// break; +// } + + hr = vboxNetFltWinNotifyCheckLowerRange(pComponent, pbShouldBind); + if (hr == S_OK) + { + /* the vboxNetFltWinNotifyCheckLowerRange ccucceeded, + * the pbShouldBind already contains the result */ + break; + } + /* we are here because of the fail, nothing else to do */ + } while (0); + + return hr; +} + + +static HRESULT vboxNetFltWinNotifyShouldBind(IN INetCfgBindingInterface *pIf, OUT bool *pbShouldBind) +{ + INetCfgComponent * pAdapterComponent; + HRESULT hr = pIf->GetLowerComponent(&pAdapterComponent); + if (hr == S_OK) + { + hr = vboxNetFltWinNotifyShouldBind(pAdapterComponent, pbShouldBind); + + pAdapterComponent->Release(); + } + else + { + NonStandardAssertBreakpoint(); + } + + return hr; +} + +static HRESULT vboxNetFltWinNotifyShouldBind(IN INetCfgBindingPath *pPath, OUT bool *pbDoBind) +{ + IEnumNetCfgBindingInterface *pEnumBindingIf; + HRESULT hr = pPath->EnumBindingInterfaces(&pEnumBindingIf); + if (hr == S_OK) + { + hr = pEnumBindingIf->Reset(); + if (hr == S_OK) + { + ULONG ulCount; + INetCfgBindingInterface *pBindingIf; + do + { + hr = pEnumBindingIf->Next(1, &pBindingIf, &ulCount); + if (hr == S_OK) + { + hr = vboxNetFltWinNotifyShouldBind(pBindingIf, pbDoBind); + + pBindingIf->Release(); + + if (hr == S_OK) + { + if (!(*pbDoBind)) + { + break; + } + } + else + { + /* break on failure */ + break; + } + } + else if (hr == S_FALSE) + { + /* no more elements */ + hr = S_OK; + break; + } + else + { + NonStandardAssertBreakpoint(); + /* break on falure */ + break; + } + } while (true); + } + else + { + NonStandardAssertBreakpoint(); + } + + pEnumBindingIf->Release(); + } + else + { + NonStandardAssertBreakpoint(); + } + + return hr; +} + +static bool vboxNetFltWinNotifyShouldBind(IN INetCfgBindingPath *pPath) +{ +#ifdef VBOXNETFLTNOTIFY_DEBUG_BIND + return VBOXNETFLTNOTIFY_DEBUG_BIND; +#else + bool bShouldBind; + HRESULT hr = vboxNetFltWinNotifyShouldBind(pPath, &bShouldBind) ; + if (hr != S_OK) + { + bShouldBind = VBOXNETFLTNOTIFY_ONFAIL_BINDDEFAULT; + } + + return bShouldBind; +#endif +} + + +/* INetCfgComponentNotifyBinding methods */ +STDMETHODIMP VBoxNetFltNobj::NotifyBindingPath(IN DWORD dwChangeFlag, IN INetCfgBindingPath *pNetCfgBP) +{ + if (!(dwChangeFlag & NCN_ENABLE) || (dwChangeFlag & NCN_REMOVE) || vboxNetFltWinNotifyShouldBind(pNetCfgBP)) + return S_OK; + return NETCFG_S_DISABLE_QUERY; +} + +STDMETHODIMP VBoxNetFltNobj::QueryBindingPath(IN DWORD dwChangeFlag, IN INetCfgBindingPath *pNetCfgBP) +{ + RT_NOREF1(dwChangeFlag); + if (vboxNetFltWinNotifyShouldBind(pNetCfgBP)) + return S_OK; + return NETCFG_S_DISABLE_QUERY; +} + + +static ATL::CComModule _Module; + +BEGIN_OBJECT_MAP(ObjectMap) + OBJECT_ENTRY(CLSID_VBoxNetFltNobj, VBoxNetFltNobj) +END_OBJECT_MAP() + +extern "C" +BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /*lpReserved*/) +{ + if (dwReason == DLL_PROCESS_ATTACH) + { + _Module.Init(ObjectMap, hInstance); + DisableThreadLibraryCalls(hInstance); + } + else if (dwReason == DLL_PROCESS_DETACH) + { + _Module.Term(); + } + return TRUE; +} + +STDAPI DllCanUnloadNow() +{ + return (_Module.GetLockCount() == 0) ? S_OK : S_FALSE; +} + +STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv) +{ + return _Module.GetClassObject(rclsid, riid, ppv); +} + +/* + * ATL::CComModule does not suport server registration/unregistration methods, + * so we need to do it manually. Since this is the only place we do registraton + * manually, we do it the quick-and-dirty way. + */ + +/* Someday we may want to log errors. */ +class AdHocRegError +{ +public: + AdHocRegError(LSTATUS rc) { RT_NOREF1(rc); }; +}; + +/* A simple wrapper on Windows registry functions. */ +class AdHocRegKey +{ +public: + AdHocRegKey(HKEY hKey) : m_hKey(hKey) {}; + AdHocRegKey(LPCWSTR pcwszName, HKEY hParent = HKEY_CLASSES_ROOT); + ~AdHocRegKey() { RegCloseKey(m_hKey); }; + + AdHocRegKey *create(LPCWSTR pcwszSubkey, LPCWSTR pcwszDefaultValue = NULL); + void remove(LPCWSTR pcwszSubkey); + void setValue(LPCWSTR pcwszName, LPCWSTR pcwszValue); + HKEY getKey(void) { return m_hKey; }; +private: + HKEY m_hKey; +}; + +AdHocRegKey::AdHocRegKey(LPCWSTR pcwszName, HKEY hParent) +{ + LSTATUS rc = RegOpenKeyExW(hParent, pcwszName, 0, KEY_ALL_ACCESS, &m_hKey); + if (rc != ERROR_SUCCESS) + throw AdHocRegError(rc); +} + +void AdHocRegKey::remove(LPCWSTR pcwszSubkey) +{ + LSTATUS rc; + WCHAR wszName[256]; + DWORD dwName; + + /* Remove all subkeys of subkey first */ + AdHocRegKey *subkey = new AdHocRegKey(pcwszSubkey, m_hKey); + for (;;) + { + /* Always ask for the first subkey, because we remove it before calling RegEnumKeyEx again */ + dwName = 255; + rc = RegEnumKeyExW(subkey->getKey(), 0, wszName, &dwName, NULL, NULL, NULL, NULL); + if (rc != ERROR_SUCCESS) + break; + subkey->remove(wszName); + } + delete subkey; + + /* Remove the subkey itself */ + rc = RegDeleteKeyW(m_hKey, pcwszSubkey); + if (rc != ERROR_SUCCESS) + throw AdHocRegError(rc); +} + +AdHocRegKey *AdHocRegKey::create(LPCWSTR pcwszSubkey, LPCWSTR pcwszDefaultValue) +{ + HKEY hSubkey; + LSTATUS rc = RegCreateKeyExW(m_hKey, pcwszSubkey, + 0 /*Reserved*/, NULL /*pszClass*/, 0 /*fOptions*/, + KEY_ALL_ACCESS, NULL /*pSecAttr*/, &hSubkey, NULL /*pdwDisposition*/); + if (rc != ERROR_SUCCESS) + throw AdHocRegError(rc); + AdHocRegKey *pSubkey = new AdHocRegKey(hSubkey); + if (pcwszDefaultValue) + pSubkey->setValue(NULL, pcwszDefaultValue); + return pSubkey; +} + +void AdHocRegKey::setValue(LPCWSTR pcwszName, LPCWSTR pcwszValue) +{ + LSTATUS rc = RegSetValueExW(m_hKey, pcwszName, 0, REG_SZ, (const BYTE *)pcwszValue, + (DWORD)((wcslen(pcwszValue) + 1) * sizeof(WCHAR))); + if (rc != ERROR_SUCCESS) + throw AdHocRegError(rc); +} + +/* + * Auxiliary class that facilitates automatic destruction of AdHocRegKey objects + * allocated in heap. No reference counting here! + */ +class AdHocRegKeyPtr +{ +public: + AdHocRegKeyPtr(AdHocRegKey *pKey) : m_pKey(pKey) {}; + ~AdHocRegKeyPtr() { delete m_pKey; }; + + AdHocRegKey *create(LPCWSTR pcwszSubkey, LPCWSTR pcwszDefaultValue = NULL) + { return m_pKey->create(pcwszSubkey, pcwszDefaultValue); }; + void remove(LPCWSTR pcwszSubkey) + { return m_pKey->remove(pcwszSubkey); }; + void setValue(LPCWSTR pcwszName, LPCWSTR pcwszValue) + { return m_pKey->setValue(pcwszName, pcwszValue); }; +private: + AdHocRegKey *m_pKey; + /* Prevent copying, since we do not support reference counting */ + AdHocRegKeyPtr(const AdHocRegKeyPtr&); + AdHocRegKeyPtr& operator=(const AdHocRegKeyPtr&); +}; + + +STDAPI DllRegisterServer() +{ + WCHAR wszModule[MAX_PATH + 1]; + if (GetModuleFileNameW(GetModuleHandleW(L"VBoxNetFltNobj"), wszModule, MAX_PATH) == 0) + return SELFREG_E_CLASS; + + try { + AdHocRegKey keyCLSID(L"CLSID"); + AdHocRegKeyPtr pkeyNobjClass(keyCLSID.create(L"{f374d1a0-bf08-4bdc-9cb2-c15ddaeef955}", + L"VirtualBox Bridged Networking Driver Notify Object v1.1")); + AdHocRegKeyPtr pkeyNobjSrv(pkeyNobjClass.create(L"InProcServer32", wszModule)); + pkeyNobjSrv.setValue(L"ThreadingModel", L"Both"); + } + catch (AdHocRegError) + { + return SELFREG_E_CLASS; + } + + try { + AdHocRegKey keyTypeLib(L"TypeLib"); + AdHocRegKeyPtr pkeyNobjLib(keyTypeLib.create(L"{2A0C94D1-40E1-439C-8FE8-24107CAB0840}\\1.1", + L"VirtualBox Bridged Networking Driver Notify Object v1.1 Type Library")); + AdHocRegKeyPtr pkeyNobjLib0(pkeyNobjLib.create(L"0\\win64", wszModule)); + AdHocRegKeyPtr pkeyNobjLibFlags(pkeyNobjLib.create(L"FLAGS", L"0")); + if (GetSystemDirectoryW(wszModule, MAX_PATH) == 0) + return SELFREG_E_TYPELIB; + AdHocRegKeyPtr pkeyNobjLibHelpDir(pkeyNobjLib.create(L"HELPDIR", wszModule)); + } + catch (AdHocRegError) + { + return SELFREG_E_CLASS; + } + + return S_OK; +} + +STDAPI DllUnregisterServer() +{ + try { + AdHocRegKey keyTypeLib(L"TypeLib"); + keyTypeLib.remove(L"{2A0C94D1-40E1-439C-8FE8-24107CAB0840}"); + } + catch (AdHocRegError) { return SELFREG_E_TYPELIB; } + + try { + AdHocRegKey keyCLSID(L"CLSID"); + keyCLSID.remove(L"{f374d1a0-bf08-4bdc-9cb2-c15ddaeef955}"); + } + catch (AdHocRegError) { return SELFREG_E_CLASS; } + + return S_OK; +} diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobj.def b/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobj.def new file mode 100644 index 00000000..74dc6ee4 --- /dev/null +++ b/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobj.def @@ -0,0 +1,32 @@ +; $Id: VBoxNetFltNobj.def $ +; @file +; VBoxNetFltNobj.def - Notify Object for Bridged Networking Driver. +; Library def file +; + +; +; Copyright (C) 2011-2019 Oracle Corporation +; +; This file is part of VirtualBox Open Source Edition (OSE), as +; available from http://www.virtualbox.org. This file is free software; +; you can redistribute it and/or modify it under the terms of the GNU +; 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. +; +; The contents of this file may alternatively be used under the terms +; of the Common Development and Distribution License Version 1.0 +; (CDDL) only, as it comes in the "COPYING.CDDL" file of the +; VirtualBox OSE 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. +; +LIBRARY VBoxNetFltNobj +EXPORTS + DllCanUnloadNow PRIVATE + DllGetClassObject PRIVATE + DllRegisterServer PRIVATE + DllUnregisterServer PRIVATE diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobj.h b/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobj.h new file mode 100644 index 00000000..e53041a1 --- /dev/null +++ b/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobj.h @@ -0,0 +1,86 @@ +/* $Id: VBoxNetFltNobj.h $ */ +/** @file + * VBoxNetFltNobj.h - Notify Object for Bridged Networking Driver. + * Used to filter Bridged Networking Driver bindings + */ +/* + * Copyright (C) 2011-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * 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. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE 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. + */ + +#ifndef VBOX_INCLUDED_SRC_VBoxNetFlt_win_nobj_VBoxNetFltNobj_h +#define VBOX_INCLUDED_SRC_VBoxNetFlt_win_nobj_VBoxNetFltNobj_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#include <iprt/win/windows.h> + +#include "VBox/com/defs.h" +#include "VBoxNetFltNobjT.h" +#include "VBoxNetFltNobjRc.h" + +#define VBOXNETFLTNOTIFY_ONFAIL_BINDDEFAULT false + +/* + * VirtualBox Bridging driver notify object. + * Needed to make our driver bind to "real" host adapters only + */ +class ATL_NO_VTABLE VBoxNetFltNobj : + public ATL::CComObjectRootEx<ATL::CComMultiThreadModel>, + public ATL::CComCoClass<VBoxNetFltNobj, &CLSID_VBoxNetFltNobj>, + public INetCfgComponentControl, + public INetCfgComponentNotifyBinding +{ +public: + VBoxNetFltNobj(); + virtual ~VBoxNetFltNobj(); + + BEGIN_COM_MAP(VBoxNetFltNobj) + COM_INTERFACE_ENTRY(INetCfgComponentControl) + COM_INTERFACE_ENTRY(INetCfgComponentNotifyBinding) + END_COM_MAP() + + // this is a "just in case" conditional, which is not defined +#ifdef VBOX_FORCE_REGISTER_SERVER + DECLARE_REGISTRY_RESOURCEID(IDR_VBOXNETFLT_NOBJ) +#endif + + /* INetCfgComponentControl methods */ + STDMETHOD(Initialize)(IN INetCfgComponent *pNetCfgComponent, IN INetCfg *pNetCfg, IN BOOL bInstalling); + STDMETHOD(ApplyRegistryChanges)(); + STDMETHOD(ApplyPnpChanges)(IN INetCfgPnpReconfigCallback *pCallback); + STDMETHOD(CancelChanges)(); + + /* INetCfgComponentNotifyBinding methods */ + STDMETHOD(NotifyBindingPath)(IN DWORD dwChangeFlag, IN INetCfgBindingPath *pNetCfgBP); + STDMETHOD(QueryBindingPath)(IN DWORD dwChangeFlag, IN INetCfgBindingPath *pNetCfgBP); +private: + + void init(IN INetCfgComponent *pNetCfgComponent, IN INetCfg *pNetCfg, IN BOOL bInstalling); + void cleanup(); + + /* these two used to maintain the component info passed to + * INetCfgComponentControl::Initialize */ + INetCfg *mpNetCfg; + INetCfgComponent *mpNetCfgComponent; + BOOL mbInstalling; +}; + +#endif /* !VBOX_INCLUDED_SRC_VBoxNetFlt_win_nobj_VBoxNetFltNobj_h */ diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobj.rc b/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobj.rc new file mode 100644 index 00000000..ce17df13 --- /dev/null +++ b/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobj.rc @@ -0,0 +1,68 @@ +/* $Id: VBoxNetFltNobj.rc $ */ +/** @file + * VBoxNetFltNobj - Resource file containing version info and icon. + */ +/* + * Copyright (C) 2011-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * 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. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE 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. + */ + +#include <windows.h> +#include <VBox/version.h> + +#include "VBoxNetFltNobjRc.h" + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VBOX_RC_FILE_VERSION + PRODUCTVERSION VBOX_RC_FILE_VERSION + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK + FILEFLAGS VBOX_RC_FILE_FLAGS + FILEOS VBOX_RC_FILE_OS + FILETYPE VBOX_RC_TYPE_DLL + FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904E4" // Lang=US English, CharSet=Windows Multilingual + BEGIN + VALUE "FileDescription", "VirtualBox Bridged Networking Driver Notify Object v1.1\0" + VALUE "InternalName", "VBoxNetFltNobj\0" + VALUE "OriginalFilename", "VBoxNetFltNobj.dll\0" + VALUE "CompanyName", VBOX_RC_COMPANY_NAME + VALUE "FileVersion", VBOX_RC_FILE_VERSION_STR + VALUE "LegalCopyright", VBOX_RC_LEGAL_COPYRIGHT + VALUE "ProductName", VBOX_RC_PRODUCT_NAME_STR + VALUE "ProductVersion", VBOX_RC_PRODUCT_VERSION_STR + VBOX_RC_MORE_STRINGS + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + +///////////////////////////////////////////////////////////////////////////// +// +// REGISTRY +// + +IDR_VBOXNETFLT_NOBJ REGISTRY "VBoxNetFltNobj.rgs" + +1 TYPELIB "VBoxNetFltNobjT.tlb" diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobj.rgs b/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobj.rgs new file mode 100644 index 00000000..d6f9bae3 --- /dev/null +++ b/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobj.rgs @@ -0,0 +1,13 @@ +HKCR +{ + NoRemove CLSID + { + ForceRemove {f374d1a0-bf08-4bdc-9cb2-c15ddaeef955} = s 'VirtualBox Bridged Networking Driver Notify Object v1.1' + { + InProcServer32 = s '%MODULE%' + { + val ThreadingModel = s 'Both' + } + } + } +}
\ No newline at end of file diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobjRc.h b/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobjRc.h new file mode 100644 index 00000000..85185513 --- /dev/null +++ b/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobjRc.h @@ -0,0 +1,36 @@ +/* $Id: VBoxNetFltNobjRc.h $ */ +/** @file + * VBoxNetFltNobjRc.h - Notify Object for Bridged Networking Driver. + * Resource definitions + */ +/* + * Copyright (C) 2011-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * 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. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE 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. + */ + +#ifndef VBOX_INCLUDED_SRC_VBoxNetFlt_win_nobj_VBoxNetFltNobjRc_h +#define VBOX_INCLUDED_SRC_VBoxNetFlt_win_nobj_VBoxNetFltNobjRc_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +/* registry script rc ID */ +#define IDR_VBOXNETFLT_NOBJ 101 + +#endif /* !VBOX_INCLUDED_SRC_VBoxNetFlt_win_nobj_VBoxNetFltNobjRc_h */ diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobjT.idl b/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobjT.idl new file mode 100644 index 00000000..c1a02e78 --- /dev/null +++ b/src/VBox/HostDrivers/VBoxNetFlt/win/nobj/VBoxNetFltNobjT.idl @@ -0,0 +1,45 @@ +/* $Id: VBoxNetFltNobjT.idl $ */ +/** @file + * VBoxNetFltNobjT.idl - Notify Object for Bridged Networking Driver, typelib definition. + */ + +/* + * Copyright (C) 2011-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * 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. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE 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. + */ + +#include <netcfgn.idl> + +[ + uuid(2a0c94d1-40e1-439c-8fe8-24107cab0840), + version(1.1), + helpstring("VirtualBox Bridged Networking Driver Notify Object v1.1 Type Library") +] +library VBoxNetFltNobjLib +{ + [ + uuid(f374d1a0-bf08-4bdc-9cb2-c15ddaeef955), + helpstring("VirtualBox Bridged Networking Driver Notify Object Class") + ] + coclass VBoxNetFltNobj + { + [restricted] interface INetCfgComponentControl; + [restricted] interface INetCfgComponentNotifyBinding; + }; +}; diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/tools/Makefile.kup b/src/VBox/HostDrivers/VBoxNetFlt/win/tools/Makefile.kup new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/VBox/HostDrivers/VBoxNetFlt/win/tools/Makefile.kup diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/tools/VBoxNetAdpInstall.cpp b/src/VBox/HostDrivers/VBoxNetFlt/win/tools/VBoxNetAdpInstall.cpp new file mode 100644 index 00000000..3731dd18 --- /dev/null +++ b/src/VBox/HostDrivers/VBoxNetFlt/win/tools/VBoxNetAdpInstall.cpp @@ -0,0 +1,292 @@ +/* $Id: VBoxNetAdpInstall.cpp $ */ +/** @file + * NetAdpInstall - VBoxNetAdp installer command line tool. + */ + +/* + * Copyright (C) 2009-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * 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. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE 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. + */ + +#include <VBox/VBoxNetCfg-win.h> +#include <VBox/VBoxDrvCfg-win.h> +#include <stdio.h> +#include <devguid.h> + +#define VBOX_NETADP_APP_NAME L"NetAdpInstall" + +#define VBOX_NETADP_HWID L"sun_VBoxNetAdp" +#ifdef NDIS60 +#define VBOX_NETADP_INF L"VBoxNetAdp6.inf" +#else /* !NDIS60 */ +#define VBOX_NETADP_INF L"VBoxNetAdp.inf" +#endif /* !NDIS60 */ + +static VOID winNetCfgLogger(LPCSTR szString) +{ + printf("%s\n", szString); +} + +static int VBoxNetAdpInstall(void) +{ + VBoxNetCfgWinSetLogging(winNetCfgLogger); + + HRESULT hr = CoInitialize(NULL); + if (SUCCEEDED(hr)) + { + wprintf(L"adding host-only interface..\n"); + + DWORD dwErr = ERROR_SUCCESS; + WCHAR MpInf[MAX_PATH]; + + if (!GetFullPathNameW(VBOX_NETADP_INF, sizeof(MpInf)/sizeof(MpInf[0]), MpInf, NULL)) + dwErr = GetLastError(); + + if (dwErr == ERROR_SUCCESS) + { + INetCfg *pnc; + LPWSTR lpszLockedBy = NULL; + hr = VBoxNetCfgWinQueryINetCfg(&pnc, TRUE, VBOX_NETADP_APP_NAME, 10000, &lpszLockedBy); + if (hr == S_OK) + { + + hr = VBoxNetCfgWinNetAdpInstall(pnc, MpInf); + + if (hr == S_OK) + { + wprintf(L"installed successfully\n"); + } + else + { + wprintf(L"error installing VBoxNetAdp (0x%x)\n", hr); + } + + VBoxNetCfgWinReleaseINetCfg(pnc, TRUE); + } + else + wprintf(L"VBoxNetCfgWinQueryINetCfg failed: hr = 0x%x\n", hr); + /* + hr = VBoxDrvCfgInfInstall(MpInf); + if (FAILED(hr)) + printf("VBoxDrvCfgInfInstall failed %#x\n", hr); + + GUID guid; + BSTR name, errMsg; + + hr = VBoxNetCfgWinCreateHostOnlyNetworkInterface (MpInf, true, &guid, &name, &errMsg); + if (SUCCEEDED(hr)) + { + ULONG ip, mask; + hr = VBoxNetCfgWinGenHostOnlyNetworkNetworkIp(&ip, &mask); + if (SUCCEEDED(hr)) + { + // ip returned by VBoxNetCfgWinGenHostOnlyNetworkNetworkIp is a network ip, + // i.e. 192.168.xxx.0, assign 192.168.xxx.1 for the hostonly adapter + ip = ip | (1 << 24); + hr = VBoxNetCfgWinEnableStaticIpConfig(&guid, ip, mask); + if (SUCCEEDED(hr)) + { + printf("installation successful\n"); + } + else + printf("VBoxNetCfgWinEnableStaticIpConfig failed: hr = 0x%x\n", hr); + } + else + printf("VBoxNetCfgWinGenHostOnlyNetworkNetworkIp failed: hr = 0x%x\n", hr); + } + else + printf("VBoxNetCfgWinCreateHostOnlyNetworkInterface failed: hr = 0x%x\n", hr); + */ + } + else + { + wprintf(L"GetFullPathNameW failed: winEr = %d\n", dwErr); + hr = HRESULT_FROM_WIN32(dwErr); + + } + CoUninitialize(); + } + else + wprintf(L"Error initializing COM (0x%x)\n", hr); + + VBoxNetCfgWinSetLogging(NULL); + + return SUCCEEDED(hr) ? 0 : 1; +} + +static int VBoxNetAdpUninstall(void) +{ + VBoxNetCfgWinSetLogging(winNetCfgLogger); + + printf("uninstalling all host-only interfaces..\n"); + + HRESULT hr = CoInitialize(NULL); + if (SUCCEEDED(hr)) + { + hr = VBoxNetCfgWinRemoveAllNetDevicesOfId(VBOX_NETADP_HWID); + if (SUCCEEDED(hr)) + { + hr = VBoxDrvCfgInfUninstallAllSetupDi(&GUID_DEVCLASS_NET, L"Net", VBOX_NETADP_HWID, 0/* could be SUOI_FORCEDELETE */); + if (SUCCEEDED(hr)) + { + printf("uninstallation successful\n"); + } + else + printf("uninstalled successfully, but failed to remove infs\n"); + } + else + printf("uninstall failed, hr = 0x%x\n", hr); + CoUninitialize(); + } + else + printf("Error initializing COM (0x%x)\n", hr); + + VBoxNetCfgWinSetLogging(NULL); + + return SUCCEEDED(hr) ? 0 : 1; +} + +static int VBoxNetAdpUpdate(void) +{ + VBoxNetCfgWinSetLogging(winNetCfgLogger); + + printf("uninstalling all host-only interfaces..\n"); + + HRESULT hr = CoInitialize(NULL); + if (SUCCEEDED(hr)) + { + BOOL fRebootRequired = FALSE; + /* + * Before we can update the driver for existing adapters we need to remove + * all old driver packages from the driver cache. Otherwise we may end up + * with both NDIS5 and NDIS6 versions of VBoxNetAdp in the cache which + * will cause all sorts of trouble. + */ + VBoxDrvCfgInfUninstallAllF(L"Net", VBOX_NETADP_HWID, SUOI_FORCEDELETE); + hr = VBoxNetCfgWinUpdateHostOnlyNetworkInterface(VBOX_NETADP_INF, &fRebootRequired, VBOX_NETADP_HWID); + if (SUCCEEDED(hr)) + { + if (fRebootRequired) + printf("!!REBOOT REQUIRED!!\n"); + printf("updated successfully\n"); + } + else + printf("update failed, hr = 0x%x\n", hr); + + CoUninitialize(); + } + else + printf("Error initializing COM (0x%x)\n", hr); + + VBoxNetCfgWinSetLogging(NULL); + + return SUCCEEDED(hr) ? 0 : 1; +} + +static int VBoxNetAdpDisable(void) +{ + VBoxNetCfgWinSetLogging(winNetCfgLogger); + + printf("disabling all host-only interfaces..\n"); + + HRESULT hr = CoInitialize(NULL); + if (SUCCEEDED(hr)) + { + hr = VBoxNetCfgWinPropChangeAllNetDevicesOfId(VBOX_NETADP_HWID, VBOXNECTFGWINPROPCHANGE_TYPE_DISABLE); + if (SUCCEEDED(hr)) + { + printf("disabling successful\n"); + } + else + printf("disable failed, hr = 0x%x\n", hr); + + CoUninitialize(); + } + else + printf("Error initializing COM (0x%x)\n", hr); + + VBoxNetCfgWinSetLogging(NULL); + + return SUCCEEDED(hr) ? 0 : 1; +} + +static int VBoxNetAdpEnable(void) +{ + VBoxNetCfgWinSetLogging(winNetCfgLogger); + + printf("enabling all host-only interfaces..\n"); + + HRESULT hr = CoInitialize(NULL); + if (SUCCEEDED(hr)) + { + hr = VBoxNetCfgWinPropChangeAllNetDevicesOfId(VBOX_NETADP_HWID, VBOXNECTFGWINPROPCHANGE_TYPE_ENABLE); + if (SUCCEEDED(hr)) + { + printf("enabling successful\n"); + } + else + printf("enabling failed, hr = 0x%x\n", hr); + + CoUninitialize(); + } + else + printf("Error initializing COM (0x%x)\n", hr); + + VBoxNetCfgWinSetLogging(NULL); + + return SUCCEEDED(hr) ? 0 : 1; +} + +static void printUsage(void) +{ + printf("host-only network adapter configuration tool\n" + " Usage: VBoxNetAdpInstall [cmd]\n" + " cmd can be one of the following values:\n" + " i - install a new host-only interface (default command)\n" + " u - uninstall all host-only interfaces\n" + " a - update the host-only driver\n" + " d - disable all host-only interfaces\n" + " e - enable all host-only interfaces\n" + " h - print this message\n"); +} + +int __cdecl main(int argc, char **argv) +{ + if (argc < 2) + return VBoxNetAdpInstall(); + if (argc > 2) + { + printUsage(); + return 1; + } + + if (!strcmp(argv[1], "i")) + return VBoxNetAdpInstall(); + if (!strcmp(argv[1], "u")) + return VBoxNetAdpUninstall(); + if (!strcmp(argv[1], "a")) + return VBoxNetAdpUpdate(); + if (!strcmp(argv[1], "d")) + return VBoxNetAdpDisable(); + if (!strcmp(argv[1], "e")) + return VBoxNetAdpEnable(); + + printUsage(); + return !strcmp(argv[1], "h"); +} diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/tools/VBoxNetAdpUninstall.cpp b/src/VBox/HostDrivers/VBoxNetFlt/win/tools/VBoxNetAdpUninstall.cpp new file mode 100644 index 00000000..7eed8fd3 --- /dev/null +++ b/src/VBox/HostDrivers/VBoxNetFlt/win/tools/VBoxNetAdpUninstall.cpp @@ -0,0 +1,89 @@ +/* $Id: VBoxNetAdpUninstall.cpp $ */ +/** @file + * NetAdpUninstall - VBoxNetAdp uninstaller command line tool + */ + +/* + * Copyright (C) 2009-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * 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. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE 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. + */ + +#include <VBox/VBoxNetCfg-win.h> +#include <VBox/VBoxDrvCfg-win.h> +#include <stdio.h> + +#include <devguid.h> + +#ifdef NDIS60 +#define VBOX_NETADP_HWID L"sun_VBoxNetAdp6" +#else /* !NDIS60 */ +#define VBOX_NETADP_HWID L"sun_VBoxNetAdp" +#endif /* !NDIS60 */ + +static VOID winNetCfgLogger (LPCSTR szString) +{ + printf("%s", szString); +} + +static int VBoxNetAdpUninstall() +{ + int r = 1; + VBoxNetCfgWinSetLogging(winNetCfgLogger); + + printf("uninstalling all Host-Only interfaces..\n"); + + HRESULT hr = CoInitialize(NULL); + if (hr == S_OK) + { + hr = VBoxNetCfgWinRemoveAllNetDevicesOfId(VBOX_NETADP_HWID); + if (hr == S_OK) + { + hr = VBoxDrvCfgInfUninstallAllSetupDi(&GUID_DEVCLASS_NET, L"Net", VBOX_NETADP_HWID, 0/* could be SUOI_FORCEDELETE */); + if (hr == S_OK) + { + printf("uninstalled successfully\n"); + } + else + { + printf("uninstalled successfully, but failed to remove infs\n"); + } + r = 0; + } + else + { + printf("uninstall failed, hr = 0x%x\n", hr); + } + + CoUninitialize(); + } + else + { + wprintf(L"Error initializing COM (0x%x)\n", hr); + } + + VBoxNetCfgWinSetLogging(NULL); + + return r; +} + +int __cdecl main(int argc, char **argv) +{ + RT_NOREF2(argc, argv); + return VBoxNetAdpUninstall(); +} diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/tools/VBoxNetFltInstall.cpp b/src/VBox/HostDrivers/VBoxNetFlt/win/tools/VBoxNetFltInstall.cpp new file mode 100644 index 00000000..f6fbe3d5 --- /dev/null +++ b/src/VBox/HostDrivers/VBoxNetFlt/win/tools/VBoxNetFltInstall.cpp @@ -0,0 +1,183 @@ +/* $Id: VBoxNetFltInstall.cpp $ */ +/** @file + * NetFltInstall - VBoxNetFlt installer command line tool + */ + +/* + * Copyright (C) 2008-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * 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. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE 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. + */ + +#include <VBox/VBoxNetCfg-win.h> +#include <devguid.h> +#include <stdio.h> + +#define NETFLT_ID L"sun_VBoxNetFlt" +#define VBOX_NETCFG_APP_NAME L"NetFltInstall" +#define VBOX_NETFLT_PT_INF L".\\VBoxNetFlt.inf" +#define VBOX_NETFLT_MP_INF L".\\VBoxNetFltM.inf" +#define VBOX_NETFLT_RETRIES 10 + + +static VOID winNetCfgLogger (LPCSTR szString) +{ + printf("%s", szString); +} + +/** Wrapper around GetfullPathNameW that will try an alternative INF location. + * + * The default location is the current directory. If not found there, the + * alternative location is the executable directory. If not found there either, + * the first alternative is present to the caller. + */ +static DWORD MyGetfullPathNameW(LPCWSTR pwszName, size_t cchFull, LPWSTR pwszFull) +{ + LPWSTR pwszFilePart; + DWORD dwSize = GetFullPathNameW(pwszName, (DWORD)cchFull, pwszFull, &pwszFilePart); + if (dwSize <= 0) + return dwSize; + + /* if it doesn't exist, see if the file exists in the same directory as the executable. */ + if (GetFileAttributesW(pwszFull) == INVALID_FILE_ATTRIBUTES) + { + WCHAR wsz[512]; + DWORD cch = GetModuleFileNameW(GetModuleHandle(NULL), &wsz[0], sizeof(wsz) / sizeof(wsz[0])); + if (cch > 0) + { + while (cch > 0 && wsz[cch - 1] != '/' && wsz[cch - 1] != '\\' && wsz[cch - 1] != ':') + cch--; + unsigned i = 0; + while (cch < sizeof(wsz) / sizeof(wsz[0])) + { + wsz[cch] = pwszFilePart[i++]; + if (!wsz[cch]) + { + dwSize = GetFullPathNameW(wsz, (DWORD)cchFull, pwszFull, NULL); + if (dwSize > 0 && GetFileAttributesW(pwszFull) != INVALID_FILE_ATTRIBUTES) + return dwSize; + break; + } + cch++; + } + } + } + + /* fallback */ + return GetFullPathNameW(pwszName, (DWORD)cchFull, pwszFull, NULL); +} + +static int VBoxNetFltInstall() +{ + WCHAR PtInf[MAX_PATH]; + WCHAR MpInf[MAX_PATH]; + INetCfg *pnc; + LPWSTR lpszLockedBy = NULL; + int r = 1; + + VBoxNetCfgWinSetLogging(winNetCfgLogger); + + HRESULT hr = CoInitialize(NULL); + if (hr == S_OK) + { + int i = 0; + do + { + hr = VBoxNetCfgWinQueryINetCfg(&pnc, TRUE, VBOX_NETCFG_APP_NAME, 10000, &lpszLockedBy); + if (hr == S_OK) + { + DWORD dwSize; + dwSize = MyGetfullPathNameW(VBOX_NETFLT_PT_INF, sizeof(PtInf)/sizeof(PtInf[0]), PtInf); + if (dwSize > 0) + { + /** @todo add size check for (sizeof(PtInf)/sizeof(PtInf[0])) == dwSize (string length in sizeof(PtInf[0])) */ + + dwSize = MyGetfullPathNameW(VBOX_NETFLT_MP_INF, sizeof(MpInf)/sizeof(MpInf[0]), MpInf); + if (dwSize > 0) + { + /** @todo add size check for (sizeof(MpInf)/sizeof(MpInf[0])) == dwSize (string length in sizeof(MpInf[0])) */ + + LPCWSTR aInfs[] = {PtInf, MpInf}; + hr = VBoxNetCfgWinNetFltInstall(pnc, aInfs, 2); + if (hr == S_OK) + { + wprintf(L"installed successfully\n"); + r = 0; + } + else + { + wprintf(L"error installing VBoxNetFlt (0x%x)\n", hr); + } + } + else + { + hr = HRESULT_FROM_WIN32(GetLastError()); + wprintf(L"error getting full inf path for VBoxNetFltM.inf (0x%x)\n", hr); + } + } + else + { + hr = HRESULT_FROM_WIN32(GetLastError()); + wprintf(L"error getting full inf path for VBoxNetFlt.inf (0x%x)\n", hr); + } + + + VBoxNetCfgWinReleaseINetCfg(pnc, TRUE); + break; + } + else if (hr == NETCFG_E_NO_WRITE_LOCK && lpszLockedBy) + { + if (i < VBOX_NETFLT_RETRIES && !wcscmp(lpszLockedBy, L"6to4svc.dll")) + { + wprintf(L"6to4svc.dll is holding the lock, retrying %d out of %d\n", ++i, VBOX_NETFLT_RETRIES); + CoTaskMemFree(lpszLockedBy); + } + else + { + wprintf(L"Error: write lock is owned by another application (%s), close the application and retry installing\n", lpszLockedBy); + r = 1; + CoTaskMemFree(lpszLockedBy); + break; + } + } + else + { + wprintf(L"Error getting the INetCfg interface (0x%x)\n", hr); + r = 1; + break; + } + } while (true); + + CoUninitialize(); + } + else + { + wprintf(L"Error initializing COM (0x%x)\n", hr); + r = 1; + } + + VBoxNetCfgWinSetLogging(NULL); + + return r; +} + +int __cdecl main(int argc, char **argv) +{ + RT_NOREF2(argc, argv); + return VBoxNetFltInstall(); +} diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/tools/VBoxNetFltUninstall.cpp b/src/VBox/HostDrivers/VBoxNetFlt/win/tools/VBoxNetFltUninstall.cpp new file mode 100644 index 00000000..a1860d04 --- /dev/null +++ b/src/VBox/HostDrivers/VBoxNetFlt/win/tools/VBoxNetFltUninstall.cpp @@ -0,0 +1,113 @@ +/* $Id: VBoxNetFltUninstall.cpp $ */ +/** @file + * NetFltUninstall - VBoxNetFlt uninstaller command line tool + */ + +/* + * Copyright (C) 2008-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * 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. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE 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. + */ + +#include <VBox/VBoxNetCfg-win.h> +#include <stdio.h> + +#define NETFLT_ID L"sun_VBoxNetFlt" +#define VBOX_NETCFG_APP_NAME L"NetFltUninstall" +#define VBOX_NETFLT_PT_INF L".\\VBoxNetFlt.inf" +#define VBOX_NETFLT_MP_INF L".\\VBoxNetFltM.inf" +#define VBOX_NETFLT_RETRIES 10 + +static VOID winNetCfgLogger (LPCSTR szString) +{ + printf("%s", szString); +} + +static int VBoxNetFltUninstall() +{ + INetCfg *pnc; + LPWSTR lpszLockedBy = NULL; + int r; + + VBoxNetCfgWinSetLogging(winNetCfgLogger); + + HRESULT hr = CoInitialize(NULL); + if (hr == S_OK) + { + int i = 0; + do + { + hr = VBoxNetCfgWinQueryINetCfg(&pnc, TRUE, VBOX_NETCFG_APP_NAME, 10000, &lpszLockedBy); + if (hr == S_OK) + { + hr = VBoxNetCfgWinNetFltUninstall(pnc); + if (hr != S_OK && hr != S_FALSE) + { + wprintf(L"error uninstalling VBoxNetFlt (0x%x)\n", hr); + r = 1; + } + else + { + wprintf(L"uninstalled successfully\n"); + r = 0; + } + + VBoxNetCfgWinReleaseINetCfg(pnc, TRUE); + break; + } + else if (hr == NETCFG_E_NO_WRITE_LOCK && lpszLockedBy) + { + if (i < VBOX_NETFLT_RETRIES && !wcscmp(lpszLockedBy, L"6to4svc.dll")) + { + wprintf(L"6to4svc.dll is holding the lock, retrying %d out of %d\n", ++i, VBOX_NETFLT_RETRIES); + CoTaskMemFree(lpszLockedBy); + } + else + { + wprintf(L"Error: write lock is owned by another application (%s), close the application and retry uninstalling\n", lpszLockedBy); + r = 1; + CoTaskMemFree(lpszLockedBy); + break; + } + } + else + { + wprintf(L"Error getting the INetCfg interface (0x%x)\n", hr); + r = 1; + break; + } + } while (true); + + CoUninitialize(); + } + else + { + wprintf(L"Error initializing COM (0x%x)\n", hr); + r = 1; + } + + VBoxNetCfgWinSetLogging(NULL); + + return r; +} + +int __cdecl main(int argc, char **argv) +{ + RT_NOREF2(argc, argv); + return VBoxNetFltUninstall(); +} diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/tools/VBoxNetLwfInstall.cpp b/src/VBox/HostDrivers/VBoxNetFlt/win/tools/VBoxNetLwfInstall.cpp new file mode 100644 index 00000000..89eeda07 --- /dev/null +++ b/src/VBox/HostDrivers/VBoxNetFlt/win/tools/VBoxNetLwfInstall.cpp @@ -0,0 +1,167 @@ +/* $Id: VBoxNetLwfInstall.cpp $ */ +/** @file + * NetLwfInstall - VBoxNetLwf installer command line tool + */ + +/* + * Copyright (C) 2014-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * 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. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE 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. + */ + +#include <VBox/VBoxNetCfg-win.h> +#include <devguid.h> +#include <stdio.h> + +#define VBOX_NETCFG_APP_NAME L"NetLwfInstall" +#define VBOX_NETLWF_INF L".\\VBoxNetLwf.inf" +#define VBOX_NETLWF_RETRIES 10 + + +static VOID winNetCfgLogger (LPCSTR szString) +{ + printf("%s", szString); +} + +/** Wrapper around GetfullPathNameW that will try an alternative INF location. + * + * The default location is the current directory. If not found there, the + * alternative location is the executable directory. If not found there either, + * the first alternative is present to the caller. + */ +static DWORD MyGetfullPathNameW(LPCWSTR pwszName, size_t cchFull, LPWSTR pwszFull) +{ + LPWSTR pwszFilePart; + DWORD dwSize = GetFullPathNameW(pwszName, (DWORD)cchFull, pwszFull, &pwszFilePart); + if (dwSize <= 0) + return dwSize; + + /* if it doesn't exist, see if the file exists in the same directory as the executable. */ + if (GetFileAttributesW(pwszFull) == INVALID_FILE_ATTRIBUTES) + { + WCHAR wsz[512]; + DWORD cch = GetModuleFileNameW(GetModuleHandle(NULL), &wsz[0], sizeof(wsz) / sizeof(wsz[0])); + if (cch > 0) + { + while (cch > 0 && wsz[cch - 1] != '/' && wsz[cch - 1] != '\\' && wsz[cch - 1] != ':') + cch--; + unsigned i = 0; + while (cch < sizeof(wsz) / sizeof(wsz[0])) + { + wsz[cch] = pwszFilePart[i++]; + if (!wsz[cch]) + { + dwSize = GetFullPathNameW(wsz, (DWORD)cchFull, pwszFull, NULL); + if (dwSize > 0 && GetFileAttributesW(pwszFull) != INVALID_FILE_ATTRIBUTES) + return dwSize; + break; + } + cch++; + } + } + } + + /* fallback */ + return GetFullPathNameW(pwszName, (DWORD)cchFull, pwszFull, NULL); +} + +static int VBoxNetLwfInstall() +{ + WCHAR Inf[MAX_PATH]; + INetCfg *pnc; + LPWSTR lpszLockedBy = NULL; + int r = 1; + + VBoxNetCfgWinSetLogging(winNetCfgLogger); + + HRESULT hr = CoInitialize(NULL); + if (hr == S_OK) + { + int i = 0; + do + { + hr = VBoxNetCfgWinQueryINetCfg(&pnc, TRUE, VBOX_NETCFG_APP_NAME, 10000, &lpszLockedBy); + if (hr == S_OK) + { + DWORD dwSize; + dwSize = MyGetfullPathNameW(VBOX_NETLWF_INF, sizeof(Inf)/sizeof(Inf[0]), Inf); + if (dwSize > 0) + { + /** @todo add size check for (sizeof(Inf)/sizeof(Inf[0])) == dwSize (string length in sizeof(Inf[0])) */ + hr = VBoxNetCfgWinNetLwfInstall(pnc, Inf); + if (hr == S_OK) + { + wprintf(L"installed successfully\n"); + r = 0; + } + else + { + wprintf(L"error installing VBoxNetLwf (0x%x)\n", hr); + } + } + else + { + hr = HRESULT_FROM_WIN32(GetLastError()); + wprintf(L"error getting full inf path for VBoxNetLwf.inf (0x%x)\n", hr); + } + + + VBoxNetCfgWinReleaseINetCfg(pnc, TRUE); + break; + } + else if (hr == NETCFG_E_NO_WRITE_LOCK && lpszLockedBy) + { + if (i < VBOX_NETLWF_RETRIES && !wcscmp(lpszLockedBy, L"6to4svc.dll")) + { + wprintf(L"6to4svc.dll is holding the lock, retrying %d out of %d\n", ++i, VBOX_NETLWF_RETRIES); + CoTaskMemFree(lpszLockedBy); + } + else + { + wprintf(L"Error: write lock is owned by another application (%s), close the application and retry installing\n", lpszLockedBy); + r = 1; + CoTaskMemFree(lpszLockedBy); + break; + } + } + else + { + wprintf(L"Error getting the INetCfg interface (0x%x)\n", hr); + r = 1; + break; + } + } while (true); + + CoUninitialize(); + } + else + { + wprintf(L"Error initializing COM (0x%x)\n", hr); + r = 1; + } + + VBoxNetCfgWinSetLogging(NULL); + + return r; +} + +int __cdecl main(int argc, char **argv) +{ + RT_NOREF2(argc, argv); + return VBoxNetLwfInstall(); +} diff --git a/src/VBox/HostDrivers/VBoxNetFlt/win/tools/VBoxNetLwfUninstall.cpp b/src/VBox/HostDrivers/VBoxNetFlt/win/tools/VBoxNetLwfUninstall.cpp new file mode 100644 index 00000000..b757289f --- /dev/null +++ b/src/VBox/HostDrivers/VBoxNetFlt/win/tools/VBoxNetLwfUninstall.cpp @@ -0,0 +1,110 @@ +/* $Id: VBoxNetLwfUninstall.cpp $ */ +/** @file + * NetLwfUninstall - VBoxNetLwf uninstaller command line tool + */ + +/* + * Copyright (C) 2014-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * 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. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE 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. + */ + +#include <VBox/VBoxNetCfg-win.h> +#include <stdio.h> + +#define VBOX_NETCFG_APP_NAME L"NetLwfUninstall" +#define VBOX_NETLWF_RETRIES 10 + +static VOID winNetCfgLogger (LPCSTR szString) +{ + printf("%s", szString); +} + +static int VBoxNetLwfUninstall() +{ + INetCfg *pnc; + LPWSTR lpszLockedBy = NULL; + int r; + + VBoxNetCfgWinSetLogging(winNetCfgLogger); + + HRESULT hr = CoInitialize(NULL); + if (hr == S_OK) + { + int i = 0; + do + { + hr = VBoxNetCfgWinQueryINetCfg(&pnc, TRUE, VBOX_NETCFG_APP_NAME, 10000, &lpszLockedBy); + if (hr == S_OK) + { + hr = VBoxNetCfgWinNetLwfUninstall(pnc); + if (hr != S_OK) + { + wprintf(L"error uninstalling VBoxNetLwf (0x%x)\n", hr); + r = 1; + } + else + { + wprintf(L"uninstalled successfully\n"); + r = 0; + } + + VBoxNetCfgWinReleaseINetCfg(pnc, TRUE); + break; + } + else if (hr == NETCFG_E_NO_WRITE_LOCK && lpszLockedBy) + { + if (i < VBOX_NETLWF_RETRIES && !wcscmp(lpszLockedBy, L"6to4svc.dll")) + { + wprintf(L"6to4svc.dll is holding the lock, retrying %d out of %d\n", ++i, VBOX_NETLWF_RETRIES); + CoTaskMemFree(lpszLockedBy); + } + else + { + wprintf(L"Error: write lock is owned by another application (%s), close the application and retry uninstalling\n", lpszLockedBy); + r = 1; + CoTaskMemFree(lpszLockedBy); + break; + } + } + else + { + wprintf(L"Error getting the INetCfg interface (0x%x)\n", hr); + r = 1; + break; + } + } while (true); + + CoUninitialize(); + } + else + { + wprintf(L"Error initializing COM (0x%x)\n", hr); + r = 1; + } + + VBoxNetCfgWinSetLogging(NULL); + + return r; +} + +int __cdecl main(int argc, char **argv) +{ + RT_NOREF2(argc, argv); + return VBoxNetLwfUninstall(); +} |