VBoxNetFltNobj.cpp revision 3331475701a5b12f98b3cfea07d5dca60072530f
/* $Id$ */
/** @file
* VBoxNetFltNobj.cpp - Notify Object for Bridged Networking Driver.
* Used to filter Bridged Networking Driver bindings
*/
/*
* Copyright (C) 2011 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.
*/
#include "VBoxNetFltNobj.h"
#include <Ntddndis.h>
#include <assert.h>
#include <stdio.h>
#include <VBoxNetFltNobjT_i.c>
//# define VBOXNETFLTNOTIFY_DEBUG_BIND
#ifdef DEBUG
# define Assert(a) assert(a)
# define AssertBreakpoint() assert(0)
#else
# define Assert(a) do{}while (0)
# define AssertBreakpoint() 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();
Assert(pNetCfg);
Assert(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)
{
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);
AssertBreakpoint();
}
CoTaskMemFree(pPnpId);
}
else
{
AssertBreakpoint();
}
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);
AssertBreakpoint();
}
}
else
{
hr = HRESULT_FROM_WIN32(winEr);
AssertBreakpoint();
}
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)
{
AssertBreakpoint();
}
RegCloseKey(InstanceKey);
}
else
{
AssertBreakpoint();
}
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
{
AssertBreakpoint();
}
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
{
AssertBreakpoint();
*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 */
AssertBreakpoint();
*pbShouldBind = true;
}
RegCloseKey(DriverKey);
}
else
{
AssertBreakpoint();
}
}
else
{
hr = S_FALSE;
}
CoTaskMemFree(pDevId);
}
else
{
AssertBreakpoint();
}
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;
AssertBreakpoint();
}
RegCloseKey(InterfacesKey);
}
else
{
hr = HRESULT_FROM_WIN32(winEr);
AssertBreakpoint();
}
RegCloseKey(DriverKey);
}
else
{
AssertBreakpoint();
}
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)
{
AssertBreakpoint();
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
{
AssertBreakpoint();
}
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
{
AssertBreakpoint();
/* break on falure */
break;
}
} while (true);
}
else
{
AssertBreakpoint();
}
pEnumBindingIf->Release();
}
else
{
AssertBreakpoint();
}
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)
{
if (vboxNetFltWinNotifyShouldBind(pNetCfgBP))
return S_OK;
return NETCFG_S_DISABLE_QUERY;
}
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);
}
STDAPI DllRegisterServer()
{
return _Module.RegisterServer(TRUE);
}
STDAPI DllUnregisterServer()
{
return _Module.UnregisterServer(TRUE);
}