DEV9: Have each backend use the internal server

This commit is contained in:
TheLastRar 2021-03-08 15:41:13 +00:00 committed by lightningterror
parent 7e35c7750e
commit 36406e2fd9
2 changed files with 351 additions and 3 deletions

View File

@ -17,6 +17,15 @@
#include <stdio.h>
#include <windows.h>
#include <winsock2.h>
#include <ws2ipdef.h>
#include <iphlpapi.h>
#include <Netcfgx.h>
#include <comdef.h>
#include <atlbase.h>
#include <devguid.h>
#include <tchar.h>
#include "tap.h"
#include "..\dev9.h"
@ -268,7 +277,279 @@ HANDLE TAPOpen(const char* device_guid)
return handle;
}
PIP_ADAPTER_ADDRESSES FindAdapterViaIndex(PIP_ADAPTER_ADDRESSES adapterList, int ifIndex)
{
PIP_ADAPTER_ADDRESSES currentAdapter = adapterList;
do
{
if (currentAdapter->IfIndex == ifIndex)
break;
currentAdapter = currentAdapter->Next;
} while (currentAdapter);
return currentAdapter;
}
//IP_ADAPTER_ADDRESSES is a structure that contains ptrs to data in other regions
//of the buffer, se we need to return both so the caller can free the buffer
//after it's finished reading the needed data from IP_ADAPTER_ADDRESSES
bool TAPGetWin32Adapter(const char* name, PIP_ADAPTER_ADDRESSES adapter, std::unique_ptr<IP_ADAPTER_ADDRESSES[]>* buffer)
{
int neededSize = 256;
std::unique_ptr<IP_ADAPTER_ADDRESSES[]> AdapterInfo = std::make_unique<IP_ADAPTER_ADDRESSES[]>(neededSize);
ULONG dwBufLen = sizeof(IP_ADAPTER_ADDRESSES) * neededSize;
PIP_ADAPTER_ADDRESSES pAdapterInfo;
//GAA_FLAG_INCLUDE_ALL_INTERFACES needed to get Tap when bridged
DWORD dwStatus = GetAdaptersAddresses(
AF_UNSPEC,
GAA_FLAG_INCLUDE_PREFIX | GAA_FLAG_INCLUDE_GATEWAYS | GAA_FLAG_INCLUDE_ALL_INTERFACES,
NULL,
AdapterInfo.get(),
&dwBufLen);
if (dwStatus == ERROR_BUFFER_OVERFLOW)
{
DevCon.WriteLn("DEV9: GetWin32Adapter() buffer too small, resizing");
//
neededSize = dwBufLen / sizeof(IP_ADAPTER_ADDRESSES) + 1;
AdapterInfo = std::make_unique<IP_ADAPTER_ADDRESSES[]>(neededSize);
dwBufLen = sizeof(IP_ADAPTER_ADDRESSES) * neededSize;
DevCon.WriteLn("DEV9: New size %i", neededSize);
dwStatus = GetAdaptersAddresses(
AF_UNSPEC,
GAA_FLAG_INCLUDE_PREFIX | GAA_FLAG_INCLUDE_GATEWAYS | GAA_FLAG_INCLUDE_ALL_INTERFACES,
NULL,
AdapterInfo.get(),
&dwBufLen);
}
if (dwStatus != ERROR_SUCCESS)
return 0;
pAdapterInfo = AdapterInfo.get();
do
{
if (0 == strcmp(pAdapterInfo->AdapterName, name))
break;
pAdapterInfo = pAdapterInfo->Next;
} while (pAdapterInfo);
if (pAdapterInfo == nullptr)
return false;
//If we are bridged, then we won't show up without GAA_FLAG_INCLUDE_ALL_INTERFACES
std::unique_ptr<IP_ADAPTER_ADDRESSES[]> AdapterInfoReduced = std::make_unique<IP_ADAPTER_ADDRESSES[]>(neededSize);
dwBufLen = sizeof(IP_ADAPTER_ADDRESSES) * neededSize;
dwStatus = GetAdaptersAddresses(
AF_UNSPEC,
GAA_FLAG_INCLUDE_PREFIX | GAA_FLAG_INCLUDE_GATEWAYS,
NULL,
AdapterInfoReduced.get(),
&dwBufLen);
if (dwStatus != ERROR_SUCCESS)
return 0;
//If we find our adapter in the reduced list, we are not bridged
if (FindAdapterViaIndex(AdapterInfoReduced.get(), pAdapterInfo->IfIndex) != nullptr)
{
*adapter = *pAdapterInfo;
buffer->swap(AdapterInfo);
return true;
}
//We must be bridged
Console.WriteLn("DEV9: Current adapter is probably bridged");
Console.WriteLn(L"DEV9: Adapter Display name: %s", pAdapterInfo->FriendlyName);
//We will need to find the bridge adapter that out adapter is
//as the IP information of the tap adapter is null
//connected to, the method used to do this is undocumented and windows 8+
//Only solution found is detailed in this MSDN fourm post by Jeffrey Tippet[MSFT], with a sectin copyied below
//Some adjustments to the method where required before this would work on my system.
//https://social.msdn.microsoft.com/Forums/vstudio/en-US/6dc9097e-0c33-427c-8e1b-9e2c81fad367/how-to-detect-if-network-interface-is-part-of-ethernet-bridge-?forum=wdk
/* To detect the newer LWF driver, it's trickier, since the binding over the NIC would be to the generic IM platform.
* Knowing that a NIC is bound to the generic IM platform tells you that it's being used for some fancy thing,
* but it doesn't tell you whether it's a bridge or an LBFO team or something more exotic.
* The way to distinguish exactly which flavor of ms_implat you have is to look at which LWF driver is bound to the *virtual miniport* above the IM driver.
* This is two steps then.
*
* 1. Given a physical NIC, you first want to determine which virtual NIC is layered over it.
* 2. Given a virtual NIC, you want to determine whether ms_bridge is bound to it.
*
* To get the first part, look through the interface stack table (GetIfStackTable). Search the stack table for any entry where the lower is the IfIndex of the physical NIC.
* For any such entry (there will probably be a few), check if that entry's upper IfIndex is the IfIndex for a virtual miniport with component ID "COMPOSITEBUS\MS_IMPLAT_MP".
* If you find such a thing, that means the physical NIC is a member of a bridge/LBFO/something-else-fancy.
* If you don't find it, then you know the NIC isn't part of the bridge that comes with Windows 8 / Windows 10.
*
* To get the second part, just use the same INetCfg code above on the *virtual* NIC's component. If the ms_bridge component is bound to the virtual NIC,
* then that virtual NIC is doing bridging. Otherwise, it's doing something else (like LBFO).
*/
//Step 1
//Find any rows that how our adapter as the lower index
//check if the upper adapter has a non-null address
//If not, we repeat the search with the upper adapter
//If multiple rows have our adapter, we check all of them
std::vector<NET_IFINDEX> potentialBridges;
std::vector<NET_IFINDEX> searchList;
searchList.push_back(pAdapterInfo->IfIndex);
int checkCount = 1;
PMIB_IFSTACK_TABLE table;
GetIfStackTable(&table);
//Note that we append to the collection during iteration
for (size_t vi = 0; vi < searchList.size(); vi++)
{
int targetIndex = searchList[vi];
for (ULONG i = 0; i < table->NumEntries; i++)
{
MIB_IFSTACK_ROW row = table->Table[i];
if (row.LowerLayerInterfaceIndex == targetIndex)
{
PIP_ADAPTER_ADDRESSES potentialAdapter = FindAdapterViaIndex(AdapterInfoReduced.get(), row.HigherLayerInterfaceIndex);
if (potentialAdapter != nullptr)
{
Console.WriteLn("DEV9: %s is possible bridge (Check 1 passed)", potentialAdapter->Description);
potentialBridges.push_back(row.HigherLayerInterfaceIndex);
}
else
searchList.push_back(row.HigherLayerInterfaceIndex);
break;
}
}
}
//Cleanup
FreeMibTable(table);
AdapterInfoReduced = nullptr;
//Step 2
HRESULT cohr = S_OK;
//Init COM
cohr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
if (!SUCCEEDED(cohr))
return false;
PIP_ADAPTER_ADDRESSES bridgeAdapter = nullptr;
//Create Instance of INetCfg
HRESULT hr = S_OK;
CComPtr<INetCfg> netcfg;
hr = netcfg.CoCreateInstance(CLSID_CNetCfg, nullptr, CLSCTX_INPROC_SERVER);
if (SUCCEEDED(hr))
{
hr = netcfg->Initialize(nullptr);
if (SUCCEEDED(hr))
{
//Get the bridge component
//The bridged adapter should have this bound
CComPtr<INetCfgComponent> bridge;
hr = netcfg->FindComponent(L"ms_bridge", &bridge);
if (SUCCEEDED(hr))
{
//Get a List of network adapters via INetCfg
CComPtr<IEnumNetCfgComponent> components;
hr = netcfg->EnumComponents(&GUID_DEVCLASS_NET, &components);
if (SUCCEEDED(hr))
{
//Search possible bridge adapters
for (size_t i = 0; i < potentialBridges.size(); i++)
{
//We need to match the adapter index to an INetCfgComponent
//We do this by matching IP_ADAPTER_ADDRESSES.AdapterName
//with the INetCfgComponent Instance GUID
PIP_ADAPTER_ADDRESSES cAdapterInfo = FindAdapterViaIndex(AdapterInfo.get(), potentialBridges[i]);
if (cAdapterInfo == nullptr || cAdapterInfo->AdapterName == nullptr)
continue;
//Convert Name to GUID
wchar_t wName[40] = {0};
mbstowcs(wName, cAdapterInfo->AdapterName, 39);
GUID nameGuid;
hr = IIDFromString(wName, &nameGuid);
if (!SUCCEEDED(hr))
continue;
//Loop through components
CComPtr<INetCfgComponent> component;
while (true)
{
component.Release(); //CComPtr must be release any held component or else we assert
if (components->Next(1, &component, nullptr) != S_OK)
break;
GUID comInstGuid;
hr = component->GetInstanceGuid(&comInstGuid);
if (SUCCEEDED(hr) && IsEqualGUID(nameGuid, comInstGuid))
{
CComHeapPtr<WCHAR> comId;
hr = component->GetId(&comId);
if (!SUCCEEDED(hr))
continue;
//The bridge adapter for Win8+ has this ComponentID
//However not every adapter with this componentID is a bridge
if (wcscmp(L"compositebus\\ms_implat_mp", comId) == 0)
{
CComHeapPtr<WCHAR> dispName;
hr = component->GetDisplayName(&dispName);
if (SUCCEEDED(hr))
Console.WriteLn(L"DEV9: %s is possible bridge (Check 2 passed)", dispName);
//Check if adapter has the ms_bridge component bound to it.
CComPtr<INetCfgComponentBindings> bindings;
hr = bridge->QueryInterface<INetCfgComponentBindings>(&bindings);
if (!SUCCEEDED(hr))
continue;
hr = bindings->IsBoundTo(component);
if (hr != S_OK)
continue;
dispName.Free();
hr = component->GetDisplayName(&dispName);
if (SUCCEEDED(hr))
Console.WriteLn(L"DEV9: %s is bridge (Check 3 passed)", dispName);
bridgeAdapter = cAdapterInfo;
break;
}
}
}
components->Reset();
if (bridgeAdapter != nullptr)
break;
}
}
}
netcfg->Uninitialize();
}
}
netcfg.Release(); //Release before CoUninitialize();
if (cohr == S_OK)
CoUninitialize();
if (bridgeAdapter != nullptr)
{
*adapter = *bridgeAdapter;
buffer->swap(AdapterInfo);
return true;
}
return false;
}
TAPAdapter::TAPAdapter()
: NetAdapter()
@ -299,6 +580,13 @@ TAPAdapter::TAPAdapter()
SetMACAddress(newMAC);
IP_ADAPTER_ADDRESSES adapter;
std::unique_ptr<IP_ADAPTER_ADDRESSES[]> buffer;
if (TAPGetWin32Adapter(config.Eth, &adapter, &buffer))
InitInternalServer(&adapter);
else
InitInternalServer(nullptr);
isActive = true;
}
@ -347,6 +635,9 @@ bool TAPAdapter::recv(NetPacket* pkt)
//sends the packet .rv :true success
bool TAPAdapter::send(NetPacket* pkt)
{
if (NetAdapter::send(pkt))
return true;
DWORD writen;
BOOL result = WriteFile(htap,
pkt->buffer,

View File

@ -25,6 +25,9 @@
#elif defined(__linux__)
#include <sys/ioctl.h>
#include <net/if.h>
#elif defined(__POSIX__)
#include <sys/types.h>
#include <ifaddrs.h>
#endif
#include <stdio.h>
@ -55,7 +58,7 @@ mac_address host_mac;
//IP_ADAPTER_ADDRESSES is a structure that contains ptrs to data in other regions
//of the buffer, se we need to return both so the caller can free the buffer
//after it's finished reading the needed data from IP_ADAPTER_ADDRESSES
bool GetWin32Adapter(const char* name, PIP_ADAPTER_ADDRESSES adapter, std::unique_ptr<IP_ADAPTER_ADDRESSES[]>* buffer)
bool PCAPGetWin32Adapter(const char* name, PIP_ADAPTER_ADDRESSES adapter, std::unique_ptr<IP_ADAPTER_ADDRESSES[]>* buffer)
{
const int guidindex = strlen("\\Device\\NPF_");
@ -105,6 +108,38 @@ bool GetWin32Adapter(const char* name, PIP_ADAPTER_ADDRESSES adapter, std::uniqu
} while (pAdapterInfo);
return false;
}
#elif defined(__POSIX__)
//getifaddrs is not POSIX, but is supported on MAC & Linux
bool PCAPGetIfAdapter(char* name, ifaddrs* adapter, ifaddrs** buffer)
{
//Note, we don't support "any" adapter, but that also fails in pcap_io_init()
ifaddrs* adapterInfo;
ifaddrs* pAdapter;
int error = getifaddrs(&adapterInfo);
if (error)
return false;
pAdapter = adapterInfo;
do
{
if (pAdapter->ifa_addr->sa_family == AF_INET && strcmp(pAdapter->ifa_name, name) == 0)
break;
pAdapter = pAdapter->ifa_next;
} while (pAdapter);
if (pAdapter != nullptr)
{
*adapter = *pAdapter;
*buffer = adapterInfo;
return true;
}
freeifaddrs(adapterInfo);
return false;
}
#endif
// Fetches the MAC address and prints it
@ -115,7 +150,7 @@ int GetMACAddress(char* adapter, mac_address* addr)
IP_ADAPTER_ADDRESSES adapterInfo;
std::unique_ptr<IP_ADAPTER_ADDRESSES[]> buffer;
if (GetWin32Adapter(adapter, &adapterInfo, &buffer))
if (PCAPGetWin32Adapter(adapter, &adapterInfo, &buffer))
{
memcpy(addr, adapterInfo.PhysicalAddress, 6);
retval = 1;
@ -332,6 +367,25 @@ PCAPAdapter::PCAPAdapter()
host_mac = hostMAC;
ps2_mac = newMAC; //Needed outside of this class
#ifdef _WIN32
IP_ADAPTER_ADDRESSES adapter;
std::unique_ptr<IP_ADAPTER_ADDRESSES[]> buffer;
if (PCAPGetWin32Adapter(config.Eth, &adapter, &buffer))
InitInternalServer(&adapter);
else
InitInternalServer(nullptr);
#elif defined(__POSIX__)
ifaddrs adapter;
ifaddrs* buffer;
if (PCAPGetIfAdapter(config.Eth, &adapter, &buffer))
{
InitInternalServer(&adapter);
freeifaddrs(buffer);
}
else
InitInternalServer(nullptr);
#endif
if (pcap_io_init(config.Eth, config.EthApi == NetApi::PCAP_Switched, newMAC) == -1)
{
SysMessage("Can't open Device '%s'\n", config.Eth);
@ -361,6 +415,9 @@ bool PCAPAdapter::recv(NetPacket* pkt)
//sends the packet .rv :true success
bool PCAPAdapter::send(NetPacket* pkt)
{
if (NetAdapter::send(pkt))
return true;
if (pcap_io_send(pkt->buffer, pkt->size))
{
return false;
@ -406,7 +463,7 @@ std::vector<AdapterEntry> PCAPAdapter::GetAdapters()
IP_ADAPTER_ADDRESSES adapterInfo;
std::unique_ptr<IP_ADAPTER_ADDRESSES[]> buffer;
if (GetWin32Adapter(d->name, &adapterInfo, &buffer))
if (PCAPGetWin32Adapter(d->name, &adapterInfo, &buffer))
entry.name = std::wstring(adapterInfo.FriendlyName);
else
{