diff --git a/pcsx2/DEV9/Win32/tap-win32.cpp b/pcsx2/DEV9/Win32/tap-win32.cpp index 441200067c..c5032f3577 100644 --- a/pcsx2/DEV9/Win32/tap-win32.cpp +++ b/pcsx2/DEV9/Win32/tap-win32.cpp @@ -17,6 +17,15 @@ #include #include +#include +#include +#include + +#include +#include +#include +#include + #include #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* buffer) +{ + int neededSize = 256; + std::unique_ptr AdapterInfo = std::make_unique(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(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 AdapterInfoReduced = std::make_unique(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 potentialBridges; + std::vector 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 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 bridge; + hr = netcfg->FindComponent(L"ms_bridge", &bridge); + + if (SUCCEEDED(hr)) + { + //Get a List of network adapters via INetCfg + CComPtr 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 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 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 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 bindings; + hr = bridge->QueryInterface(&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 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, diff --git a/pcsx2/DEV9/pcap_io.cpp b/pcsx2/DEV9/pcap_io.cpp index e696ba81f1..6b6f1a4324 100644 --- a/pcsx2/DEV9/pcap_io.cpp +++ b/pcsx2/DEV9/pcap_io.cpp @@ -25,6 +25,9 @@ #elif defined(__linux__) #include #include +#elif defined(__POSIX__) +#include +#include #endif #include @@ -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* buffer) +bool PCAPGetWin32Adapter(const char* name, PIP_ADAPTER_ADDRESSES adapter, std::unique_ptr* 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 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 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 PCAPAdapter::GetAdapters() IP_ADAPTER_ADDRESSES adapterInfo; std::unique_ptr buffer; - if (GetWin32Adapter(d->name, &adapterInfo, &buffer)) + if (PCAPGetWin32Adapter(d->name, &adapterInfo, &buffer)) entry.name = std::wstring(adapterInfo.FriendlyName); else {