netkvm-prot: add protocol state machine

Add logic to the user-mode protocol service to replace and
extend the functionality of the notification object:
- install VIOPROT for all the network adapters
- disable all other protocols for relevant VF only when needed
- enable all the protocols on netkvm disable
- enable all the protocol on VIOPROT uninstall
- ... (see in the source code)

Signed-off-by: Yuri Benditovich <[email protected]>
This commit is contained in:
Yuri Benditovich authored and Yan Vugenfirer committed 2023-07-02 12:57:58 +03:00
1 parent 042ac27247
commit 705aaea93d
1 file changed
+691 -10
+691 -10
View File
@@ -29,37 +29,718 @@
#include "stdafx.h"
class CProtocolServiceImplementation : public CServiceImplementation
/*
The protocol service replaces and extends the notification object.
Unlike previous implementation when the VIOPROT was enabled only
on especially defined set of adapters, now the VIOPROT is installed and
enabled on all the ethernet adapters. The service is responsible for:
- disable all other protocols/filters only when needed (when the adapter
has the same MAC as virtio-net adapter)
- enable all other protocols when the protocol is uninstalled
- make standby netkvm adapter functional after several seconds if the VF
is not present and reconnect via VF when the VF comes
- enable all other protocols on VF when the netkvm is disabled
*/
class CNetCfg
{
public:
CProtocolServiceImplementation() : CServiceImplementation(_T("netkvmp")) {}
CNetCfg()
{
hr = CoCreateInstance(CLSID_CNetCfg, NULL, CLSCTX_SERVER, IID_INetCfg, (LPVOID*)&m_NetCfg);
if (!m_NetCfg) {
return;
}
hr = m_NetCfg->QueryInterface(IID_INetCfgLock, (LPVOID*)&m_NetCfgLock);
if (!m_NetCfgLock) {
return;
}
m_Usable = m_NetCfgLock->AcquireWriteLock(5000, L"netkvmp service", NULL) == S_OK;
if (m_Usable) {
hr = m_NetCfg->Initialize(NULL);
if (hr != S_OK) {
m_NetCfgLock->ReleaseWriteLock();
m_Usable = false;
}
}
}
~CNetCfg()
{
if (!m_Usable) {
return;
}
if (m_Modified)
{
Log("Applying binding changes");
m_NetCfg->Apply();
}
m_NetCfgLock->ReleaseWriteLock();
m_NetCfg->Uninitialize();
}
bool Usable() const { return m_Usable; }
void EnableVioProtOnly(const CString& Name, bool Enable)
{
CString sVioProt = L"vioprot";
CComPtr<INetCfgClass> netClass;
hr = m_NetCfg->QueryNetCfgClass(&GUID_DEVCLASS_NET, IID_INetCfgClass, (LPVOID*)&netClass);
if (hr != S_OK) {
return;
}
CComPtr<IEnumNetCfgComponent> netEnum;
hr = netClass->EnumComponents(&netEnum);
if (hr != S_OK) {
return;
}
do {
CComPtr<INetCfgComponent> adapter;
hr = netEnum->Next(1, &adapter, NULL);
if (hr != S_OK) {
break;
}
LPWSTR id = NULL;
hr = adapter->GetDisplayName(&id);
if (hr != S_OK) {
break;
}
bool found = !Name.CompareNoCase(id);
CoTaskMemFree(id);
if (found) {
Log("found %S", Name.GetString());
CComPtr<INetCfgComponentBindings> bindings;
CComPtr<IEnumNetCfgBindingPath> paths;
hr = adapter->QueryInterface(IID_INetCfgComponentBindings, (LPVOID*)&bindings);
if (hr != S_OK) {
return;
}
hr = bindings->EnumBindingPaths(EBP_ABOVE, &paths);
if (!paths) {
return;
}
do
{
CComPtr<INetCfgBindingPath> path;
hr = paths->Next(1, &path, NULL);
if (hr != S_OK) {
break;
}
bool enabled = path->IsEnabled() == S_OK;
CComPtr<IEnumNetCfgBindingInterface> enumBindingIf;
hr = path->EnumBindingInterfaces(&enumBindingIf);
if (hr == S_OK) {
CComPtr<INetCfgBindingInterface> bindingIf;
hr = enumBindingIf->Next(1, &bindingIf, NULL);
if (hr == S_OK)
{
LPWSTR upperId = NULL, lowerId = NULL;
CComPtr<INetCfgComponent> upper, lower;
bindingIf->GetUpperComponent(&upper);
bindingIf->GetLowerComponent(&lower);
if (upper) upper->GetId(&upperId);
if (lower) lower->GetId(&lowerId);
if (upperId && lowerId && lower == adapter)
{
bool bIsVioProt = !sVioProt.CompareNoCase(upperId);
// vioprot should be enabled always, all the rest - if 'Enable{OnlyVioProt}==false'
bool bShouldBeEnabled = !Enable || bIsVioProt;
Log("%sabled U:%S L:%S (should be %sabled)",
enabled ? "en" : "dis", upperId, lowerId,
bShouldBeEnabled ? "en" : "dis");
if (bShouldBeEnabled != enabled)
{
hr = path->Enable(bShouldBeEnabled);
if (hr != S_OK)
{
Log("Can't %sable hr=%X", bShouldBeEnabled ? "en" : "dis", hr);
}
else
{
m_Modified = true;
}
}
}
}
}
} while (true);
break;
}
} while (true);
}
private:
CComPtr<INetCfg> m_NetCfg;
CComPtr<INetCfgLock> m_NetCfgLock;
bool m_Usable = false;
bool m_Modified = false;
HRESULT hr;
};
class CMACString
{
public:
CMACString(const UCHAR* Address)
{
const char chars[] = "0123456789ABCDEF";
for (int i = 0; i < 6; ++i)
{
UCHAR c = Address[i];
m_Buffer[i * 3] = chars[c >> 4];
m_Buffer[i * 3 + 1] = chars[c & 0x0f];
m_Buffer[i * 3 + 2] = ':';
}
m_Buffer[sizeof(m_Buffer) - 1] = 0;
}
const char* Get() { return m_Buffer; }
private:
char m_Buffer[6 * 3] = {};
};
// we can't use GetAdaptersTable
// as it does not allow to enumerate adapters
// when both ms_tcpip and ms_tcpip6 are disabled
class CInterfaceTable
{
public:
CInterfaceTable()
{
GetIfTable2(&m_Table);
}
void CheckBinding(ULONG index, bool OnlyVioProt)
{
CNetCfg cfg;
for (ULONG i = 0; m_Table && i < m_Table->NumEntries; ++i)
{
auto& row = m_Table->Table[i];
if (row.Type == IF_TYPE_ETHERNET_CSMACD &&
row.MediaConnectState == MediaConnectStateConnected &&
!row.InterfaceAndOperStatusFlags.FilterInterface &&
!row.InterfaceAndOperStatusFlags.NotMediaConnected)
{
// Description - adapter name
// Alias - connection name
Log("IF %d (%S)(%S)", row.InterfaceIndex, row.Description, row.Alias);
if (index == INFINITE || index == row.InterfaceIndex)
{
cfg.EnableVioProtOnly(row.Description, OnlyVioProt);
}
}
}
}
~CInterfaceTable()
{
FreeMibTable(m_Table);
}
private:
PMIB_IF_TABLE2 m_Table = NULL;
};
class CDeviceNotificationOwner
{
public:
CDeviceNotificationOwner() {}
// return false only on CM_NOTIFY_ACTION_DEVICEQUERYREMOVE and only when needed
virtual bool Notification(CM_NOTIFY_ACTION action, PCM_NOTIFY_EVENT_DATA data, DWORD dataSize) = 0;
};
class CDeviceNotification
{
public:
CDeviceNotification(CDeviceNotificationOwner& owner) :
m_Owner(owner)
{
}
bool Register(CM_NOTIFY_FILTER* filter)
{
CONFIGRET cr = CM_Register_Notification(filter, this,
[](HCMNOTIFICATION h, PVOID Context, CM_NOTIFY_ACTION Action, PCM_NOTIFY_EVENT_DATA EventData, DWORD EventDataSize) -> DWORD
{
CDeviceNotification* obj = (CDeviceNotification*)Context;
DWORD res = obj->Notification(Action, EventData, EventDataSize) ? ERROR_SUCCESS : ERROR_CANCELLED;
if (res != ERROR_SUCCESS)
{
Log("WARNING: returning %d from PnP notification", res);
UNREFERENCED_PARAMETER(h);
}
return res;
},
&m_Notification);
if (!m_Notification)
{
Log("%s: failed to register, cr %d", __FUNCTION__, cr);
}
return m_Notification != NULL;
}
~CDeviceNotification()
{
if (m_Notification)
{
CM_Unregister_Notification(m_Notification);
}
m_Notification = NULL;
}
bool Notification(CM_NOTIFY_ACTION action, PCM_NOTIFY_EVENT_DATA data, DWORD dataSize)
{
Log("%s: action %s", __FUNCTION__, Name<CM_NOTIFY_ACTION>(action));
return m_Owner.Notification(action, data, dataSize);
}
private:
HCMNOTIFICATION m_Notification = NULL;
CDeviceNotificationOwner& m_Owner;
};
class CNetworkDeviceNotification : public CDeviceNotification
{
public:
CNetworkDeviceNotification(CDeviceNotificationOwner& owner) :
CDeviceNotification(owner)
{
m_Filter.cbSize = sizeof(m_Filter);
m_Filter.FilterType = CM_NOTIFY_FILTER_TYPE_DEVICEINTERFACE;
m_Filter.u.DeviceInterface.ClassGuid = GUID_DEVINTERFACE_NET;
Register(&m_Filter);
}
CM_NOTIFY_FILTER m_Filter;
};
class CNetkvmDeviceFile
{
public:
CNetkvmDeviceFile()
{
LPCWSTR devName = L"\\\\.\\" NETKVM_DEVICE_NAME;
ULONG access = GENERIC_READ | GENERIC_WRITE, share = FILE_SHARE_READ | FILE_SHARE_WRITE;
m_Handle = CreateFile(devName, access, share,
NULL, OPEN_EXISTING, 0, NULL);
if (m_Handle == INVALID_HANDLE_VALUE) {
//Log("can't open %S(%d)\n", devName, GetLastError());
m_Handle = NULL;
}
}
~CNetkvmDeviceFile()
{
if (m_Handle) {
CloseHandle(m_Handle);
}
}
bool ControlGet(ULONG Code, PVOID InBuf, ULONG InSize)
{
return Control(Code, NULL, 0, InBuf, InSize);
}
bool ControlSet(ULONG Code, PVOID OutBuf, ULONG OutSize)
{
return Control(Code, OutBuf, OutSize, NULL, 0);
}
bool Control(ULONG Code, PVOID InBuf, ULONG InSize, PVOID OutBuf, ULONG OutSize)
{
return DeviceIoControl(m_Handle, Code, InBuf, InSize, OutBuf, OutSize, &m_Returned, NULL);
}
bool Usable() const { return m_Handle; }
ULONG Returned() const { return m_Returned; }
protected:
HANDLE m_Handle;
ULONG m_Returned = 0;
};
static void CheckBinding(ULONG index, bool OnlyVioProt)
{
CInterfaceTable t;
t.CheckBinding(index, OnlyVioProt);
}
class CVirtioAdapter
{
public:
CVirtioAdapter(UCHAR *Mac = NULL)
{
if (Mac)
{
RtlCopyMemory(m_MacAddress, Mac, sizeof(m_MacAddress));
}
}
ULONG m_Count = 0;
typedef enum _tAction { acNone, acOn, acOff } tAction;
tAdapterState m_State = asUnknown;
UCHAR m_MacAddress[6];
ULONG m_VfIndex = INFINITE;
const tAdapterState m_TargetStates[128] =
{
/* 0: Virtio 0, IsStandby 0, VirtioLink 0, SuppressLink 0, Started 0, HasVf 0, VfLink 0 */ asUnknown,
/* 1: Virtio 1, IsStandby 0, VirtioLink 0, SuppressLink 0, Started 0, HasVf 0, VfLink 0 */ asStandalone,
/* 2: Virtio 0, IsStandby 1, VirtioLink 0, SuppressLink 0, Started 0, HasVf 0, VfLink 0 */ asUnknown,
/* 3: Virtio 1, IsStandby 1, VirtioLink 0, SuppressLink 0, Started 0, HasVf 0, VfLink 0 */ asAloneInactive,
/* 4: Virtio 0, IsStandby 0, VirtioLink 1, SuppressLink 0, Started 0, HasVf 0, VfLink 0 */ asUnknown,
/* 5: Virtio 1, IsStandby 0, VirtioLink 1, SuppressLink 0, Started 0, HasVf 0, VfLink 0 */ asStandalone,
/* 6: Virtio 0, IsStandby 1, VirtioLink 1, SuppressLink 0, Started 0, HasVf 0, VfLink 0 */ asUnknown,
/* 7: Virtio 1, IsStandby 1, VirtioLink 1, SuppressLink 0, Started 0, HasVf 0, VfLink 0 */ asAloneActive,
/* 8: Virtio 0, IsStandby 0, VirtioLink 0, SuppressLink 1, Started 0, HasVf 0, VfLink 0 */ asUnknown,
/* 9: Virtio 1, IsStandby 0, VirtioLink 0, SuppressLink 1, Started 0, HasVf 0, VfLink 0 */ asStandalone,
/* 10: Virtio 0, IsStandby 1, VirtioLink 0, SuppressLink 1, Started 0, HasVf 0, VfLink 0 */ asUnknown,
/* 11: Virtio 1, IsStandby 1, VirtioLink 0, SuppressLink 1, Started 0, HasVf 0, VfLink 0 */ asAloneInactive,
/* 12: Virtio 0, IsStandby 0, VirtioLink 1, SuppressLink 1, Started 0, HasVf 0, VfLink 0 */ asUnknown,
/* 13: Virtio 1, IsStandby 0, VirtioLink 1, SuppressLink 1, Started 0, HasVf 0, VfLink 0 */ asStandalone,
/* 14: Virtio 0, IsStandby 1, VirtioLink 1, SuppressLink 1, Started 0, HasVf 0, VfLink 0 */ asUnknown,
/* 15: Virtio 1, IsStandby 1, VirtioLink 1, SuppressLink 1, Started 0, HasVf 0, VfLink 0 */ asAloneActive,
/* 16: Virtio 0, IsStandby 0, VirtioLink 0, SuppressLink 0, Started 1, HasVf 0, VfLink 0 */ asUnknown,
/* 17: Virtio 1, IsStandby 0, VirtioLink 0, SuppressLink 0, Started 1, HasVf 0, VfLink 0 */ asStandalone,
/* 18: Virtio 0, IsStandby 1, VirtioLink 0, SuppressLink 0, Started 1, HasVf 0, VfLink 0 */ asUnknown,
/* 19: Virtio 1, IsStandby 1, VirtioLink 0, SuppressLink 0, Started 1, HasVf 0, VfLink 0 */ asAloneInactive,
/* 20: Virtio 0, IsStandby 0, VirtioLink 1, SuppressLink 0, Started 1, HasVf 0, VfLink 0 */ asUnknown,
/* 21: Virtio 1, IsStandby 0, VirtioLink 1, SuppressLink 0, Started 1, HasVf 0, VfLink 0 */ asStandalone,
/* 22: Virtio 0, IsStandby 1, VirtioLink 1, SuppressLink 0, Started 1, HasVf 0, VfLink 0 */ asUnknown,
/* 23: Virtio 1, IsStandby 1, VirtioLink 1, SuppressLink 0, Started 1, HasVf 0, VfLink 0 */ asAloneActive,
/* 24: Virtio 0, IsStandby 0, VirtioLink 0, SuppressLink 1, Started 1, HasVf 0, VfLink 0 */ asUnknown,
/* 25: Virtio 1, IsStandby 0, VirtioLink 0, SuppressLink 1, Started 1, HasVf 0, VfLink 0 */ asStandalone,
/* 26: Virtio 0, IsStandby 1, VirtioLink 0, SuppressLink 1, Started 1, HasVf 0, VfLink 0 */ asUnknown,
/* 27: Virtio 1, IsStandby 1, VirtioLink 0, SuppressLink 1, Started 1, HasVf 0, VfLink 0 */ asAloneInactive,
/* 28: Virtio 0, IsStandby 0, VirtioLink 1, SuppressLink 1, Started 1, HasVf 0, VfLink 0 */ asUnknown,
/* 29: Virtio 1, IsStandby 0, VirtioLink 1, SuppressLink 1, Started 1, HasVf 0, VfLink 0 */ asStandalone,
/* 30: Virtio 0, IsStandby 1, VirtioLink 1, SuppressLink 1, Started 1, HasVf 0, VfLink 0 */ asUnknown,
/* 31: Virtio 1, IsStandby 1, VirtioLink 1, SuppressLink 1, Started 1, HasVf 0, VfLink 0 */ asAloneActive,
/* 32: Virtio 0, IsStandby 0, VirtioLink 0, SuppressLink 0, Started 0, HasVf 1, VfLink 0 */ asAbsent,
/* 33: Virtio 1, IsStandby 0, VirtioLink 0, SuppressLink 0, Started 0, HasVf 1, VfLink 0 */ asStandalone,
/* 34: Virtio 0, IsStandby 1, VirtioLink 0, SuppressLink 0, Started 0, HasVf 1, VfLink 0 */ asUnknown,
/* 35: Virtio 1, IsStandby 1, VirtioLink 0, SuppressLink 0, Started 0, HasVf 1, VfLink 0 */ asAloneInactive,
/* 36: Virtio 0, IsStandby 0, VirtioLink 1, SuppressLink 0, Started 0, HasVf 1, VfLink 0 */ asUnknown,
/* 37: Virtio 1, IsStandby 0, VirtioLink 1, SuppressLink 0, Started 0, HasVf 1, VfLink 0 */ asStandalone,
/* 38: Virtio 0, IsStandby 1, VirtioLink 1, SuppressLink 0, Started 0, HasVf 1, VfLink 0 */ asUnknown,
/* 39: Virtio 1, IsStandby 1, VirtioLink 1, SuppressLink 0, Started 0, HasVf 1, VfLink 0 */ asAloneInactive,
/* 40: Virtio 0, IsStandby 0, VirtioLink 0, SuppressLink 1, Started 0, HasVf 1, VfLink 0 */ asUnknown,
/* 41: Virtio 1, IsStandby 0, VirtioLink 0, SuppressLink 1, Started 0, HasVf 1, VfLink 0 */ asStandalone,
/* 42: Virtio 0, IsStandby 1, VirtioLink 0, SuppressLink 1, Started 0, HasVf 1, VfLink 0 */ asUnknown,
/* 43: Virtio 1, IsStandby 1, VirtioLink 0, SuppressLink 1, Started 0, HasVf 1, VfLink 0 */ asAloneInactive,
/* 44: Virtio 0, IsStandby 0, VirtioLink 1, SuppressLink 1, Started 0, HasVf 1, VfLink 0 */ asUnknown,
/* 45: Virtio 1, IsStandby 0, VirtioLink 1, SuppressLink 1, Started 0, HasVf 1, VfLink 0 */ asStandalone,
/* 46: Virtio 0, IsStandby 1, VirtioLink 1, SuppressLink 1, Started 0, HasVf 1, VfLink 0 */ asUnknown,
/* 47: Virtio 1, IsStandby 1, VirtioLink 1, SuppressLink 1, Started 0, HasVf 1, VfLink 0 */ asAloneInactive,
/* 48: Virtio 0, IsStandby 0, VirtioLink 0, SuppressLink 0, Started 1, HasVf 1, VfLink 0 */ asUnknown,
/* 49: Virtio 1, IsStandby 0, VirtioLink 0, SuppressLink 0, Started 1, HasVf 1, VfLink 0 */ asStandalone,
/* 50: Virtio 0, IsStandby 1, VirtioLink 0, SuppressLink 0, Started 1, HasVf 1, VfLink 0 */ asUnknown,
/* 51: Virtio 1, IsStandby 1, VirtioLink 0, SuppressLink 0, Started 1, HasVf 1, VfLink 0 */ asAloneInactive,
/* 52: Virtio 0, IsStandby 0, VirtioLink 1, SuppressLink 0, Started 1, HasVf 1, VfLink 0 */ asUnknown,
/* 53: Virtio 1, IsStandby 0, VirtioLink 1, SuppressLink 0, Started 1, HasVf 1, VfLink 0 */ asStandalone,
/* 54: Virtio 0, IsStandby 1, VirtioLink 1, SuppressLink 0, Started 1, HasVf 1, VfLink 0 */ asUnknown,
/* 55: Virtio 1, IsStandby 1, VirtioLink 1, SuppressLink 0, Started 1, HasVf 1, VfLink 0 */ asAloneInactive,
/* 56: Virtio 0, IsStandby 0, VirtioLink 0, SuppressLink 1, Started 1, HasVf 1, VfLink 0 */ asUnknown,
/* 57: Virtio 1, IsStandby 0, VirtioLink 0, SuppressLink 1, Started 1, HasVf 1, VfLink 0 */ asStandalone,
/* 58: Virtio 0, IsStandby 1, VirtioLink 0, SuppressLink 1, Started 1, HasVf 1, VfLink 0 */ asUnknown,
/* 59: Virtio 1, IsStandby 1, VirtioLink 0, SuppressLink 1, Started 1, HasVf 1, VfLink 0 */ asAloneInactive,
/* 60: Virtio 0, IsStandby 0, VirtioLink 1, SuppressLink 1, Started 1, HasVf 1, VfLink 0 */ asUnknown,
/* 61: Virtio 1, IsStandby 0, VirtioLink 1, SuppressLink 1, Started 1, HasVf 1, VfLink 0 */ asStandalone,
/* 62: Virtio 0, IsStandby 1, VirtioLink 1, SuppressLink 1, Started 1, HasVf 1, VfLink 0 */ asUnknown,
/* 63: Virtio 1, IsStandby 1, VirtioLink 1, SuppressLink 1, Started 1, HasVf 1, VfLink 0 */ asBoundInactive,
/* 64: Virtio 0, IsStandby 0, VirtioLink 0, SuppressLink 0, Started 0, HasVf 0, VfLink 1 */ asUnknown,
/* 65: Virtio 1, IsStandby 0, VirtioLink 0, SuppressLink 0, Started 0, HasVf 0, VfLink 1 */ asUnknown,
/* 66: Virtio 0, IsStandby 1, VirtioLink 0, SuppressLink 0, Started 0, HasVf 0, VfLink 1 */ asUnknown,
/* 67: Virtio 1, IsStandby 1, VirtioLink 0, SuppressLink 0, Started 0, HasVf 0, VfLink 1 */ asUnknown,
/* 68: Virtio 0, IsStandby 0, VirtioLink 1, SuppressLink 0, Started 0, HasVf 0, VfLink 1 */ asUnknown,
/* 69: Virtio 1, IsStandby 0, VirtioLink 1, SuppressLink 0, Started 0, HasVf 0, VfLink 1 */ asUnknown,
/* 70: Virtio 0, IsStandby 1, VirtioLink 1, SuppressLink 0, Started 0, HasVf 0, VfLink 1 */ asUnknown,
/* 71: Virtio 1, IsStandby 1, VirtioLink 1, SuppressLink 0, Started 0, HasVf 0, VfLink 1 */ asUnknown,
/* 72: Virtio 0, IsStandby 0, VirtioLink 0, SuppressLink 1, Started 0, HasVf 0, VfLink 1 */ asUnknown,
/* 73: Virtio 1, IsStandby 0, VirtioLink 0, SuppressLink 1, Started 0, HasVf 0, VfLink 1 */ asUnknown,
/* 74: Virtio 0, IsStandby 1, VirtioLink 0, SuppressLink 1, Started 0, HasVf 0, VfLink 1 */ asUnknown,
/* 75: Virtio 1, IsStandby 1, VirtioLink 0, SuppressLink 1, Started 0, HasVf 0, VfLink 1 */ asUnknown,
/* 76: Virtio 0, IsStandby 0, VirtioLink 1, SuppressLink 1, Started 0, HasVf 0, VfLink 1 */ asUnknown,
/* 77: Virtio 1, IsStandby 0, VirtioLink 1, SuppressLink 1, Started 0, HasVf 0, VfLink 1 */ asUnknown,
/* 78: Virtio 0, IsStandby 1, VirtioLink 1, SuppressLink 1, Started 0, HasVf 0, VfLink 1 */ asUnknown,
/* 79: Virtio 1, IsStandby 1, VirtioLink 1, SuppressLink 1, Started 0, HasVf 0, VfLink 1 */ asUnknown,
/* 80: Virtio 0, IsStandby 0, VirtioLink 0, SuppressLink 0, Started 1, HasVf 0, VfLink 1 */ asUnknown,
/* 81: Virtio 1, IsStandby 0, VirtioLink 0, SuppressLink 0, Started 1, HasVf 0, VfLink 1 */ asUnknown,
/* 82: Virtio 0, IsStandby 1, VirtioLink 0, SuppressLink 0, Started 1, HasVf 0, VfLink 1 */ asUnknown,
/* 83: Virtio 1, IsStandby 1, VirtioLink 0, SuppressLink 0, Started 1, HasVf 0, VfLink 1 */ asUnknown,
/* 84: Virtio 0, IsStandby 0, VirtioLink 1, SuppressLink 0, Started 1, HasVf 0, VfLink 1 */ asUnknown,
/* 85: Virtio 1, IsStandby 0, VirtioLink 1, SuppressLink 0, Started 1, HasVf 0, VfLink 1 */ asUnknown,
/* 86: Virtio 0, IsStandby 1, VirtioLink 1, SuppressLink 0, Started 1, HasVf 0, VfLink 1 */ asUnknown,
/* 87: Virtio 1, IsStandby 1, VirtioLink 1, SuppressLink 0, Started 1, HasVf 0, VfLink 1 */ asUnknown,
/* 88: Virtio 0, IsStandby 0, VirtioLink 0, SuppressLink 1, Started 1, HasVf 0, VfLink 1 */ asUnknown,
/* 89: Virtio 1, IsStandby 0, VirtioLink 0, SuppressLink 1, Started 1, HasVf 0, VfLink 1 */ asUnknown,
/* 90: Virtio 0, IsStandby 1, VirtioLink 0, SuppressLink 1, Started 1, HasVf 0, VfLink 1 */ asUnknown,
/* 91: Virtio 1, IsStandby 1, VirtioLink 0, SuppressLink 1, Started 1, HasVf 0, VfLink 1 */ asUnknown,
/* 92: Virtio 0, IsStandby 0, VirtioLink 1, SuppressLink 1, Started 1, HasVf 0, VfLink 1 */ asUnknown,
/* 93: Virtio 1, IsStandby 0, VirtioLink 1, SuppressLink 1, Started 1, HasVf 0, VfLink 1 */ asUnknown,
/* 94: Virtio 0, IsStandby 1, VirtioLink 1, SuppressLink 1, Started 1, HasVf 0, VfLink 1 */ asUnknown,
/* 95: Virtio 1, IsStandby 1, VirtioLink 1, SuppressLink 1, Started 1, HasVf 0, VfLink 1 */ asUnknown,
/* 96: Virtio 0, IsStandby 0, VirtioLink 0, SuppressLink 0, Started 0, HasVf 1, VfLink 1 */ asAbsent,
/* 97: Virtio 1, IsStandby 0, VirtioLink 0, SuppressLink 0, Started 0, HasVf 1, VfLink 1 */ asStandalone,
/* 98: Virtio 0, IsStandby 1, VirtioLink 0, SuppressLink 0, Started 0, HasVf 1, VfLink 1 */ asUnknown,
/* 99: Virtio 1, IsStandby 1, VirtioLink 0, SuppressLink 0, Started 0, HasVf 1, VfLink 1 */ asBoundInactive,
/* 100: Virtio 0, IsStandby 0, VirtioLink 1, SuppressLink 0, Started 0, HasVf 1, VfLink 1 */ asUnknown,
/* 101: Virtio 1, IsStandby 0, VirtioLink 1, SuppressLink 0, Started 0, HasVf 1, VfLink 1 */ asStandalone,
/* 102: Virtio 0, IsStandby 1, VirtioLink 1, SuppressLink 0, Started 0, HasVf 1, VfLink 1 */ asUnknown,
/* 103: Virtio 1, IsStandby 1, VirtioLink 1, SuppressLink 0, Started 0, HasVf 1, VfLink 1 */ asBoundActive,
/* 104: Virtio 0, IsStandby 0, VirtioLink 0, SuppressLink 1, Started 0, HasVf 1, VfLink 1 */ asUnknown,
/* 105: Virtio 1, IsStandby 0, VirtioLink 0, SuppressLink 1, Started 0, HasVf 1, VfLink 1 */ asStandalone,
/* 106: Virtio 0, IsStandby 1, VirtioLink 0, SuppressLink 1, Started 0, HasVf 1, VfLink 1 */ asUnknown,
/* 107: Virtio 1, IsStandby 1, VirtioLink 0, SuppressLink 1, Started 0, HasVf 1, VfLink 1 */ asBoundInactive,
/* 108: Virtio 0, IsStandby 0, VirtioLink 1, SuppressLink 1, Started 0, HasVf 1, VfLink 1 */ asUnknown,
/* 109: Virtio 1, IsStandby 0, VirtioLink 1, SuppressLink 1, Started 0, HasVf 1, VfLink 1 */ asStandalone,
/* 110: Virtio 0, IsStandby 1, VirtioLink 1, SuppressLink 1, Started 0, HasVf 1, VfLink 1 */ asUnknown,
/* 111: Virtio 1, IsStandby 1, VirtioLink 1, SuppressLink 1, Started 0, HasVf 1, VfLink 1 */ asBoundInitial,
/* 112: Virtio 0, IsStandby 0, VirtioLink 0, SuppressLink 0, Started 1, HasVf 1, VfLink 1 */ asAbsent,
/* 113: Virtio 1, IsStandby 0, VirtioLink 0, SuppressLink 0, Started 1, HasVf 1, VfLink 1 */ asStandalone,
/* 114: Virtio 0, IsStandby 1, VirtioLink 0, SuppressLink 0, Started 1, HasVf 1, VfLink 1 */ asUnknown,
/* 115: Virtio 1, IsStandby 1, VirtioLink 0, SuppressLink 0, Started 1, HasVf 1, VfLink 1 */ asBoundInactive,
/* 116: Virtio 0, IsStandby 0, VirtioLink 1, SuppressLink 0, Started 1, HasVf 1, VfLink 1 */ asUnknown,
/* 117: Virtio 1, IsStandby 0, VirtioLink 1, SuppressLink 0, Started 1, HasVf 1, VfLink 1 */ asStandalone,
/* 118: Virtio 0, IsStandby 1, VirtioLink 1, SuppressLink 0, Started 1, HasVf 1, VfLink 1 */ asUnknown,
/* 119: Virtio 1, IsStandby 1, VirtioLink 1, SuppressLink 0, Started 1, HasVf 1, VfLink 1 */ asBoundActive,
/* 120: Virtio 0, IsStandby 0, VirtioLink 0, SuppressLink 1, Started 1, HasVf 1, VfLink 1 */ asUnknown,
/* 121: Virtio 1, IsStandby 0, VirtioLink 0, SuppressLink 1, Started 1, HasVf 1, VfLink 1 */ asStandalone,
/* 122: Virtio 0, IsStandby 1, VirtioLink 0, SuppressLink 1, Started 1, HasVf 1, VfLink 1 */ asUnknown,
/* 123: Virtio 1, IsStandby 1, VirtioLink 0, SuppressLink 1, Started 1, HasVf 1, VfLink 1 */ asBoundInactive,
/* 124: Virtio 0, IsStandby 0, VirtioLink 1, SuppressLink 1, Started 1, HasVf 1, VfLink 1 */ asUnknown,
/* 125: Virtio 1, IsStandby 0, VirtioLink 1, SuppressLink 1, Started 1, HasVf 1, VfLink 1 */ asStandalone,
/* 126: Virtio 0, IsStandby 1, VirtioLink 1, SuppressLink 1, Started 1, HasVf 1, VfLink 1 */ asUnknown,
/* 127: Virtio 1, IsStandby 1, VirtioLink 1, SuppressLink 1, Started 1, HasVf 1, VfLink 1 */ asBoundActive,
};
bool Match(UCHAR* Mac)
{
return !memcmp(m_MacAddress, Mac, sizeof(m_MacAddress));
}
void SetState(tAdapterState State, const NETKVMD_ADAPTER& a)
{
tAction action = acNone;
m_Count = 0;
switch (State)
{
case asStandalone:
// if SuppressLink or Started is set - this is our error
// if VF present - probably we need to unbind it from everything
break;
case asAloneInactive:
// virtio and standby are on, no vf, virtio link is off
// if Suppressed is set, better to clear it
if (a.SuppressLink) action = acOn;
break;
case asAloneActive:
// virtio, standby, virtio link are on, no vf
// if Suppressed is set, must clear it
if (a.SuppressLink) action = acOn;
break;
case asBoundInactive:
// virtio, standby, vf, but virtio link is off
// wait for carrier
// if suppressed is set, need to clear it
if (a.SuppressLink) action = acOn;
break;
case asBoundInitial:
// virtio, standby, vf, suppress, not started
// for example after netkvm parameters change
action = acOn;
CheckBinding(a.VfIfIndex, true);
break;
case asBoundActive:
// working failover
if (!a.SuppressLink && !a.Started)
{
// VF comes when virtio becomes active after initial timeout
action = acOff;
m_Count = INFINITE;
CheckBinding(a.VfIfIndex, true);
}
break;
case asAbsent:
// working VF without virtio
// VF should be bound to all the default protocols (virtio is optional)
CheckBinding(a.VfIfIndex, false);
break;
default:
break;
}
m_State = State;
NETKVMD_SET_LINK sl;
RtlCopyMemory(sl.MacAddress, m_MacAddress, sizeof(m_MacAddress));
sl.LinkOn = action == acOn ? 1 : 0;
if (action == acOn || action == acOff)
{
CNetkvmDeviceFile d;
d.ControlSet(IOCTL_NETKVMD_SET_LINK, &sl, sizeof(sl));
}
}
void Update(const NETKVMD_ADAPTER& a)
{
ULONG index = 0;
CMACString s(a.MacAddress);
// here we convert seven booleans to 7-bit index
// state is determined as m_TargetStates[index]
if (a.Virtio) index |= 1;
if (a.IsStandby) index |= 2;
if (a.VirtioLink) index |= 4;
if (a.SuppressLink) index |= 8;
if (a.Started) index |= 16;
if (a.HasVf)
{
index |= 32;
m_VfIndex = a.VfIfIndex;
}
else
{
m_VfIndex = INFINITE;
}
if (a.VfLink) index |= 64;
tAdapterState State = m_TargetStates[index];
if (State != m_State || m_Count == INFINITE)
{
Log("[%s] v%d, sb%d, l%d, su%d, st%d, vf%d, vfidx %d, vl%d",
s.Get(), a.Virtio, a.IsStandby, a.VirtioLink, a.SuppressLink, a.Started, a.HasVf, a.VfIfIndex, a.VfLink);
Log("[%s] %s => %s", s.Get(), Name<tAdapterState>(m_State), Name<tAdapterState>(State));
SetState(State, a);
}
else
{
m_Count++;
}
}
void Dump()
{
CMACString s(m_MacAddress);
Log("[%s] %s vfIdx %d", s.Get(), Name<tAdapterState>(m_State), m_VfIndex);
}
void PreRemove()
{
if ((m_State == asBoundInactive || m_State == asBoundActive) &&
m_VfIndex != INFINITE)
{
CheckBinding(m_VfIndex, false);
}
}
};
class CVirtioAdaptersArray : public CAtlArray<CVirtioAdapter>
{
public:
void RemoveAdapter(UINT Index)
{
CVirtioAdapter& a = GetAt(Index);
a.PreRemove();
RemoveAt(Index);
}
void RemoveAllAdapters()
{
for (UINT i = 0; i < GetCount(); ++i)
{
CVirtioAdapter& a = GetAt(i);
a.PreRemove();
}
RemoveAll();
}
};
class CProtocolServiceImplementation :
public CServiceImplementation,
public CThreadOwner
{
public:
CProtocolServiceImplementation() :
CServiceImplementation(_T("netkvmp")),
m_ThreadEvent(false)
{}
enum { ctlDump = 128 };
protected:
#if 0
virtual bool OnStart() override
{
StartThread();
return true;
}
#endif
virtual DWORD ControlHandler(DWORD dwControl, DWORD dwEventType, LPVOID lpEventData)
{
DWORD res = NO_ERROR;
switch (dwControl)
{
case 0xffffffff:
case ctlDump:
Dump();
return res;
default:
res = __super::ControlHandler(dwControl, dwEventType, lpEventData);
break;
}
res = __super::ControlHandler(dwControl, dwEventType, lpEventData);
return res;
}
virtual bool OnStop() override
{
CProcessRunner runner(false, 0);
CString cmd = BinaryPath();
cmd += _T(" x");
runner.RunProcess(cmd);
StopThread();
m_ThreadEvent.Set();
return true;
}
virtual void ThreadProc()
{
CoInitialize(NULL);
do {
if (ThreadState() != tsRunning)
break;
SyncVirtioAdapters();
m_ThreadEvent.Wait(3000);
} while (true);
m_Adapters.RemoveAllAdapters();
CoUninitialize();
}
void Dump()
{
CMutexProtect pr(m_AdaptersMutex);
for (UINT i = 0; i < m_Adapters.GetCount(); ++i)
{
m_Adapters[i].Dump();
}
Log("Done (%d adapters)", m_Adapters.GetCount());
}
private:
CEvent m_ThreadEvent;
CVirtioAdaptersArray m_Adapters;
CMutex m_AdaptersMutex;
private:
NETKVMD_ADAPTER m_IoctlBuffer[256];
void SyncVirtioAdapters()
{
CMutexProtect pr(m_AdaptersMutex);
NETKVMD_ADAPTER* adapters = m_IoctlBuffer;
ULONG n = 0;
// device open-close scope
{
CNetkvmDeviceFile d;
if (!d.Usable()) {
m_Adapters.RemoveAllAdapters();
}
if (!d.ControlGet(IOCTL_NETKVMD_QUERY_ADAPTERS, m_IoctlBuffer, sizeof(m_IoctlBuffer)))
{
m_Adapters.RemoveAllAdapters();
}
else
{
n = d.Returned() / sizeof(NETKVMD_ADAPTER);
}
}
// update existing adapters, add new ones
for (ULONG i = 0; i < n; ++i)
{
bool found = false;
for (ULONG j = 0; !found && j < m_Adapters.GetCount(); ++j)
{
found = m_Adapters[j].Match(adapters[i].MacAddress);
if (!found) continue;
m_Adapters[j].Update(adapters[i]);
}
if (found) continue;
CVirtioAdapter a(adapters[i].MacAddress);
a.Update(adapters[i]);
m_Adapters.Add(a);
}
// remove all non-present adapters
for (ULONG i = 0; i < m_Adapters.GetCount(); ++i)
{
bool found = false;
for (ULONG j = 0; !found && j < n; ++j)
{
found = m_Adapters[i].Match(adapters[j].MacAddress);
}
if (found) continue;
m_Adapters.RemoveAdapter(i);
i--;
}
}
};
static CProtocolServiceImplementation DummyService;