2026-01-12 05:18:12 -05:00
// SPDX-FileCopyrightText: 2002-2026 PCSX2 Dev Team
2024-07-30 13:42:36 +02:00
// SPDX-License-Identifier: GPL-3.0+
2020-10-03 21:03:04 +02:00
2021-12-19 00:33:30 +10:00
#include "common/RedtapeWindows.h"
2022-12-25 15:57:09 +10:00
#include "common/RedtapeWilCom.h"
2022-02-06 15:20:38 +00:00
#include "common/StringUtil.h"
2021-12-19 00:33:30 +10:00
2025-01-15 21:26:10 +00:00
#include "fmt/format.h"
2022-05-18 23:27:23 +10:00
2013-03-19 22:01:41 +00:00
#include <stdio.h>
2022-11-27 12:51:57 +10:00
#include <WinSock2.h>
#include <WS2tcpip.h>
2021-03-08 15:41:13 +00:00
#include <iphlpapi.h>
#include <Netcfgx.h>
#include <devguid.h>
2020-09-22 21:00:10 +02:00
#include <tchar.h>
2013-03-19 22:01:41 +00:00
#include "tap.h"
2022-02-06 16:28:21 +00:00
#include "DEV9/DEV9.h"
2013-03-19 22:01:41 +00:00
#include <string>
2021-07-07 22:22:31 +02:00
#include <wil/com.h>
#include <wil/resource.h>
2023-01-16 20:34:19 +00:00
#include "DEV9/PacketReader/MAC_Address.h"
2024-05-19 18:06:07 +01:00
#include "DEV9/AdapterUtils.h"
2023-01-16 20:34:19 +00:00
2013-03-19 22:01:41 +00:00
//=============
// TAP IOCTLs
//=============
2020-10-03 19:34:31 +02:00
#define TAP_CONTROL_CODE(request, method) \
CTL_CODE(FILE_DEVICE_UNKNOWN, request, method, FILE_ANY_ACCESS)
2013-03-19 22:01:41 +00:00
2020-10-29 10:27:54 +01:00
// clang-format off
#define TAP_IOCTL_GET_MAC TAP_CONTROL_CODE(1, METHOD_BUFFERED)
#define TAP_IOCTL_GET_VERSION TAP_CONTROL_CODE(2, METHOD_BUFFERED)
#define TAP_IOCTL_GET_MTU TAP_CONTROL_CODE(3, METHOD_BUFFERED)
#define TAP_IOCTL_GET_INFO TAP_CONTROL_CODE(4, METHOD_BUFFERED)
2020-10-03 19:34:31 +02:00
#define TAP_IOCTL_CONFIG_POINT_TO_POINT TAP_CONTROL_CODE(5, METHOD_BUFFERED)
2020-10-29 10:27:54 +01:00
#define TAP_IOCTL_SET_MEDIA_STATUS TAP_CONTROL_CODE(6, METHOD_BUFFERED)
#define TAP_IOCTL_CONFIG_DHCP_MASQ TAP_CONTROL_CODE(7, METHOD_BUFFERED)
#define TAP_IOCTL_GET_LOG_LINE TAP_CONTROL_CODE(8, METHOD_BUFFERED)
#define TAP_IOCTL_CONFIG_DHCP_SET_OPT TAP_CONTROL_CODE(9, METHOD_BUFFERED)
// clang-format on
2013-03-19 22:01:41 +00:00
//=================
// Registry keys
//=================
2020-10-03 21:03:04 +02:00
#define ADAPTER_KEY L"SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}"
2013-03-19 22:01:41 +00:00
2020-10-03 21:03:04 +02:00
#define NETWORK_CONNECTIONS_KEY L"SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}"
2013-03-19 22:01:41 +00:00
//======================
// Filesystem prefixes
//======================
#define USERMODEDEVICEDIR "\\\\.\\Global\\"
2020-10-03 19:34:31 +02:00
#define TAPSUFFIX ".tap"
2013-03-19 22:01:41 +00:00
2020-09-22 21:00:10 +02:00
#define TAP_COMPONENT_ID "tap0901"
2013-03-19 22:01:41 +00:00
2020-09-22 21:00:10 +02:00
bool IsTAPDevice ( const TCHAR * guid )
2013-03-19 22:01:41 +00:00
{
2021-07-07 22:22:31 +02:00
wil :: unique_hkey netcard_key ;
if ( RegOpenKeyEx ( HKEY_LOCAL_MACHINE , ADAPTER_KEY , 0 , KEY_READ , netcard_key . put ()) != ERROR_SUCCESS )
2020-09-22 21:00:10 +02:00
return false ;
2013-03-19 22:01:41 +00:00
2021-07-07 22:22:31 +02:00
int i = 0 ;
2020-09-22 21:00:10 +02:00
for (;;)
2013-03-19 22:01:41 +00:00
{
2020-09-22 21:00:10 +02:00
TCHAR enum_name [ 256 ];
TCHAR unit_string [ 256 ];
TCHAR component_id_string [] = _T ( "ComponentId" );
TCHAR component_id [ 256 ];
TCHAR net_cfg_instance_id_string [] = _T ( "NetCfgInstanceId" );
TCHAR net_cfg_instance_id [ 256 ];
DWORD data_type ;
2013-03-19 22:01:41 +00:00
2021-07-07 22:22:31 +02:00
DWORD len = std :: size ( enum_name );
LSTATUS status = RegEnumKeyEx ( netcard_key . get (), i , enum_name , & len , nullptr , nullptr , nullptr , nullptr );
2013-03-19 22:01:41 +00:00
2020-09-22 21:00:10 +02:00
if ( status == ERROR_NO_MORE_ITEMS )
break ;
else if ( status != ERROR_SUCCESS )
return false ;
2021-07-07 22:22:31 +02:00
_stprintf_s ( unit_string , _T ( "%s \\ %s" ), ADAPTER_KEY , enum_name );
2020-09-22 21:00:10 +02:00
2021-07-07 22:22:31 +02:00
wil :: unique_hkey unit_key ;
status = RegOpenKeyEx ( HKEY_LOCAL_MACHINE , unit_string , 0 , KEY_READ , unit_key . put ());
2020-09-22 21:00:10 +02:00
if ( status != ERROR_SUCCESS )
{
return false ;
}
else
{
len = sizeof ( component_id );
2021-07-07 22:22:31 +02:00
status = RegQueryValueEx ( unit_key . get (), component_id_string , nullptr , & data_type ,
2021-09-26 16:38:49 +01:00
( LPBYTE ) component_id , & len );
2020-09-22 21:00:10 +02:00
if ( ! ( status != ERROR_SUCCESS || data_type != REG_SZ ))
2013-03-19 22:01:41 +00:00
{
2020-09-22 21:00:10 +02:00
len = sizeof ( net_cfg_instance_id );
2021-07-07 22:22:31 +02:00
status = RegQueryValueEx ( unit_key . get (), net_cfg_instance_id_string , nullptr , & data_type ,
2021-09-26 16:38:49 +01:00
( LPBYTE ) net_cfg_instance_id , & len );
2013-03-19 22:01:41 +00:00
2020-09-22 21:00:10 +02:00
if ( status == ERROR_SUCCESS && data_type == REG_SZ )
{
// tap_ovpnconnect, tap0901 or root\tap, no clue why
2020-10-03 21:03:04 +02:00
if (( ! wcsncmp ( component_id , L "tap" , 3 ) || ! wcsncmp ( component_id , L "root \\ tap" , 8 )) && ! _tcscmp ( net_cfg_instance_id , guid ))
2013-03-19 22:01:41 +00:00
{
2020-09-22 21:00:10 +02:00
return true ;
2013-03-19 22:01:41 +00:00
}
}
}
2020-09-22 21:00:10 +02:00
}
++ i ;
2013-03-19 22:01:41 +00:00
}
2020-09-22 21:00:10 +02:00
return false ;
2013-03-19 22:01:41 +00:00
}
2021-01-12 00:52:02 +00:00
std :: vector < AdapterEntry > TAPAdapter :: GetAdapters ()
2013-03-19 22:01:41 +00:00
{
2021-01-12 00:52:02 +00:00
std :: vector < AdapterEntry > tap_nic ;
2020-09-22 21:00:10 +02:00
DWORD len ;
DWORD cSubKeys = 0 ;
2013-03-19 22:01:41 +00:00
2021-07-07 22:22:31 +02:00
wil :: unique_hkey control_net_key ;
LSTATUS status = RegOpenKeyEx ( HKEY_LOCAL_MACHINE , NETWORK_CONNECTIONS_KEY , 0 , KEY_READ | KEY_QUERY_VALUE ,
2021-09-26 16:38:49 +01:00
control_net_key . put ());
2013-03-19 22:01:41 +00:00
2020-09-22 21:00:10 +02:00
if ( status != ERROR_SUCCESS )
2021-01-12 00:52:02 +00:00
return tap_nic ;
2013-03-19 22:01:41 +00:00
2021-07-07 22:22:31 +02:00
status = RegQueryInfoKey ( control_net_key . get (), nullptr , nullptr , nullptr , & cSubKeys , nullptr , nullptr ,
2021-09-26 16:38:49 +01:00
nullptr , nullptr , nullptr , nullptr , nullptr );
2020-09-22 21:00:10 +02:00
if ( status != ERROR_SUCCESS )
2021-01-12 00:52:02 +00:00
return tap_nic ;
2020-09-22 21:00:10 +02:00
for ( DWORD i = 0 ; i < cSubKeys ; i ++ )
2013-03-19 22:01:41 +00:00
{
2020-09-22 21:00:10 +02:00
TCHAR enum_name [ 256 ];
TCHAR connection_string [ 256 ];
TCHAR name_data [ 256 ];
DWORD name_type ;
const TCHAR name_string [] = _T ( "Name" );
2013-03-19 22:01:41 +00:00
2021-07-07 22:22:31 +02:00
len = std :: size ( enum_name );
status = RegEnumKeyEx ( control_net_key . get (), i , enum_name , & len , nullptr , nullptr , nullptr , nullptr );
2020-09-22 21:00:10 +02:00
if ( status != ERROR_SUCCESS )
continue ;
2021-07-07 22:22:31 +02:00
_stprintf_s ( connection_string , _T ( "%s \\ %s \\ Connection" ),
2021-09-26 16:38:49 +01:00
NETWORK_CONNECTIONS_KEY , enum_name );
2020-09-22 21:00:10 +02:00
2021-07-07 22:22:31 +02:00
wil :: unique_hkey connection_key ;
status = RegOpenKeyEx ( HKEY_LOCAL_MACHINE , connection_string , 0 , KEY_READ , connection_key . put ());
2020-09-22 21:00:10 +02:00
if ( status == ERROR_SUCCESS )
2013-03-19 22:01:41 +00:00
{
2020-09-22 21:00:10 +02:00
len = sizeof ( name_data );
2021-07-07 22:22:31 +02:00
status = RegQueryValueEx ( connection_key . get (), name_string , nullptr , & name_type , ( LPBYTE ) name_data ,
2021-09-26 16:38:49 +01:00
& len );
2020-09-22 21:00:10 +02:00
if ( status != ERROR_SUCCESS || name_type != REG_SZ )
2013-03-19 22:01:41 +00:00
{
2020-09-22 21:00:10 +02:00
continue ;
}
else
{
if ( IsTAPDevice ( enum_name ))
{
2021-01-12 00:52:02 +00:00
AdapterEntry t ;
2022-02-06 15:20:38 +00:00
t . type = Pcsx2Config :: DEV9Options :: NetApi :: TAP ;
t . name = StringUtil :: WideStringToUTF8String ( std :: wstring ( name_data ));
t . guid = StringUtil :: WideStringToUTF8String ( std :: wstring ( enum_name ));
2021-01-12 00:52:02 +00:00
tap_nic . push_back ( t );
2020-09-22 21:00:10 +02:00
}
2013-03-19 22:01:41 +00:00
}
}
}
2020-09-22 21:00:10 +02:00
return tap_nic ;
2013-03-19 22:01:41 +00:00
}
2021-10-25 23:29:18 +01:00
AdapterOptions TAPAdapter :: GetAdapterOptions ()
{
return AdapterOptions :: None ;
}
2023-01-16 20:34:19 +00:00
static int TAPGetMACAddress ( HANDLE handle , PacketReader :: MAC_Address * addr )
2020-12-29 22:57:54 +00:00
{
DWORD len = 0 ;
return DeviceIoControl ( handle , TAP_IOCTL_GET_MAC ,
2021-09-26 16:38:49 +01:00
addr , 6 ,
addr , 6 , & len , NULL );
2020-12-29 22:57:54 +00:00
}
2013-03-19 22:01:41 +00:00
//Set the connection status
static int TAPSetStatus ( HANDLE handle , int status )
{
2020-12-29 22:57:54 +00:00
DWORD len = 0 ;
2013-03-19 22:01:41 +00:00
2020-10-03 19:34:31 +02:00
return DeviceIoControl ( handle , TAP_IOCTL_SET_MEDIA_STATUS ,
2021-09-26 16:38:49 +01:00
& status , sizeof ( status ),
& status , sizeof ( status ), & len , NULL );
2013-03-19 22:01:41 +00:00
}
//Open the TAP adapter and set the connection to enabled :)
2022-02-07 23:38:50 +00:00
HANDLE TAPOpen ( const std :: string & device_guid )
2013-03-19 22:01:41 +00:00
{
2020-10-03 19:34:31 +02:00
struct
{
unsigned long major ;
unsigned long minor ;
unsigned long debug ;
} version ;
2025-12-24 22:38:20 +01:00
DWORD version_len ;
2013-03-19 22:01:41 +00:00
2022-02-07 23:38:50 +00:00
std :: string device_path = USERMODEDEVICEDIR + device_guid + TAPSUFFIX ;
2013-03-19 22:01:41 +00:00
2021-07-07 22:22:31 +02:00
wil :: unique_hfile handle ( CreateFileA (
2022-02-07 23:38:50 +00:00
device_path . c_str (),
2020-10-03 19:34:31 +02:00
GENERIC_READ | GENERIC_WRITE ,
0 ,
0 ,
OPEN_EXISTING ,
FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED ,
2021-07-07 22:22:31 +02:00
0 ));
2013-03-19 22:01:41 +00:00
2021-07-07 22:22:31 +02:00
if ( ! handle )
2020-10-03 19:34:31 +02:00
{
return INVALID_HANDLE_VALUE ;
}
2013-03-19 22:01:41 +00:00
2023-06-16 20:36:18 +02:00
const BOOL bret = DeviceIoControl ( handle . get (), TAP_IOCTL_GET_VERSION ,
2021-09-26 16:38:49 +01:00
& version , sizeof ( version ),
2025-12-24 22:38:20 +01:00
& version , sizeof ( version ), & version_len , NULL );
2013-03-19 22:01:41 +00:00
2020-10-03 19:34:31 +02:00
if ( bret == FALSE )
{
return INVALID_HANDLE_VALUE ;
}
2013-03-19 22:01:41 +00:00
2021-07-07 22:22:31 +02:00
if ( ! TAPSetStatus ( handle . get (), TRUE ))
2020-10-03 19:34:31 +02:00
{
return INVALID_HANDLE_VALUE ;
}
2021-07-07 22:22:31 +02:00
return handle . release ();
2013-03-19 22:01:41 +00:00
}
2021-03-08 15:41:13 +00:00
PIP_ADAPTER_ADDRESSES FindAdapterViaIndex ( PIP_ADAPTER_ADDRESSES adapterList , int ifIndex )
{
PIP_ADAPTER_ADDRESSES currentAdapter = adapterList ;
do
{
if ( currentAdapter -> IfIndex == ifIndex )
break ;
2013-03-19 22:01:41 +00:00
2021-03-08 15:41:13 +00:00
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
2024-05-19 18:06:07 +01:00
bool TAPGetWin32Adapter ( const std :: string & name , PIP_ADAPTER_ADDRESSES adapter , AdapterUtils :: AdapterBuffer * buffer )
2021-03-08 15:41:13 +00:00
{
//GAA_FLAG_INCLUDE_ALL_INTERFACES needed to get Tap when bridged
2024-05-19 18:06:07 +01:00
AdapterUtils :: AdapterBuffer adapterInfo ;
PIP_ADAPTER_ADDRESSES pAdapterFirst = AdapterUtils :: GetAllAdapters ( & adapterInfo , true );
if ( pAdapterFirst == nullptr )
return false ;
2021-03-08 15:41:13 +00:00
2024-05-19 18:06:07 +01:00
PIP_ADAPTER_ADDRESSES pAdapter = pAdapterFirst ;
2021-03-08 15:41:13 +00:00
do
{
2024-05-19 18:06:07 +01:00
if ( 0 == strcmp ( pAdapter -> AdapterName , name . c_str ()))
2021-03-08 15:41:13 +00:00
break ;
2024-05-19 18:06:07 +01:00
pAdapter = pAdapter -> Next ;
} while ( pAdapter );
2021-03-08 15:41:13 +00:00
2024-05-19 18:06:07 +01:00
if ( pAdapter == nullptr )
2021-03-08 15:41:13 +00:00
return false ;
//If we are bridged, then we won't show up without GAA_FLAG_INCLUDE_ALL_INTERFACES
2024-05-19 18:06:07 +01:00
AdapterUtils :: AdapterBuffer adapterInfoReduced ;
PIP_ADAPTER_ADDRESSES pAdapterReducedFirst = AdapterUtils :: GetAllAdapters ( & adapterInfoReduced , false );
2021-03-08 15:41:13 +00:00
//If we find our adapter in the reduced list, we are not bridged
2024-05-19 18:06:07 +01:00
if ( FindAdapterViaIndex ( pAdapterReducedFirst , pAdapter -> IfIndex ) != nullptr )
2021-03-08 15:41:13 +00:00
{
2024-05-19 18:06:07 +01:00
* adapter = * pAdapter ;
buffer -> swap ( adapterInfo );
2021-03-08 15:41:13 +00:00
return true ;
}
//We must be bridged
Console . WriteLn ( "DEV9: Current adapter is probably bridged" );
2024-05-19 18:06:07 +01:00
Console . WriteLn ( fmt :: format ( "DEV9: Adapter Display name: {}" , StringUtil :: WideStringToUTF8String ( pAdapter -> FriendlyName )));
2021-03-08 15:41:13 +00:00
//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.
2022-09-13 21:20:25 -05:00
*
2021-03-08 15:41:13 +00:00
* 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.
2022-09-13 21:20:25 -05:00
*
2021-03-08 15:41:13 +00:00
* 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.
2022-09-13 21:20:25 -05:00
*
2021-03-08 15:41:13 +00:00
* 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 ;
2024-05-19 18:06:07 +01:00
searchList . push_back ( pAdapter -> IfIndex );
2021-03-08 15:41:13 +00:00
PMIB_IFSTACK_TABLE table ;
GetIfStackTable ( & table );
//Note that we append to the collection during iteration
for ( size_t vi = 0 ; vi < searchList . size (); vi ++ )
{
for ( ULONG i = 0 ; i < table -> NumEntries ; i ++ )
{
2023-06-16 20:36:18 +02:00
int targetIndex = searchList [ vi ];
2021-03-08 15:41:13 +00:00
MIB_IFSTACK_ROW row = table -> Table [ i ];
if ( row . LowerLayerInterfaceIndex == targetIndex )
{
2024-05-19 18:06:07 +01:00
const PIP_ADAPTER_ADDRESSES potentialAdapter = FindAdapterViaIndex ( pAdapterReducedFirst , row . HigherLayerInterfaceIndex );
2021-03-08 15:41:13 +00:00
if ( potentialAdapter != nullptr )
{
2022-05-18 23:27:23 +10:00
Console . WriteLn ( fmt :: format ( "DEV9: {} is possible bridge (Check 1 passed)" , StringUtil :: WideStringToUTF8String ( potentialAdapter -> Description )));
2021-03-08 15:41:13 +00:00
potentialBridges . push_back ( row . HigherLayerInterfaceIndex );
}
else
searchList . push_back ( row . HigherLayerInterfaceIndex );
break ;
}
}
}
//Cleanup
FreeMibTable ( table );
2024-05-19 18:06:07 +01:00
pAdapterReducedFirst = nullptr ;
adapterInfoReduced . reset ();
2021-03-08 15:41:13 +00:00
//Step 2
//Init COM
2022-02-13 19:30:41 +00:00
HRESULT cohr = CoInitializeEx ( nullptr , COINIT_MULTITHREADED );
if ( cohr == RPC_E_CHANGED_MODE )
cohr = CoInitializeEx ( nullptr , COINIT_APARTMENTTHREADED );
2026-06-08 16:10:00 +02:00
if ( FAILED ( cohr ))
2021-03-08 15:41:13 +00:00
return false ;
2026-06-08 16:10:00 +02:00
wil :: unique_couninitialize_call uninit ;
2021-03-08 15:41:13 +00:00
PIP_ADAPTER_ADDRESSES bridgeAdapter = nullptr ;
//Create Instance of INetCfg
2021-07-07 22:22:31 +02:00
if ( auto netcfg = wil :: CoCreateInstanceNoThrow < INetCfg > ( CLSID_CNetCfg ))
2021-03-08 15:41:13 +00:00
{
2021-07-07 22:22:31 +02:00
HRESULT hr = netcfg -> Initialize ( nullptr );
2021-03-08 15:41:13 +00:00
if ( SUCCEEDED ( hr ))
{
//Get the bridge component
//The bridged adapter should have this bound
2021-07-07 22:22:31 +02:00
wil :: com_ptr_nothrow < INetCfgComponent > bridge ;
hr = netcfg -> FindComponent ( L "ms_bridge" , bridge . put ());
2021-03-08 15:41:13 +00:00
if ( SUCCEEDED ( hr ))
{
//Get a List of network adapters via INetCfg
2021-07-07 22:22:31 +02:00
wil :: com_ptr_nothrow < IEnumNetCfgComponent > components ;
hr = netcfg -> EnumComponents ( & GUID_DEVCLASS_NET , components . put ());
2021-03-08 15:41:13 +00:00
if ( SUCCEEDED ( hr ))
{
//Search possible bridge adapters
2021-07-07 22:22:31 +02:00
for ( const auto & index : potentialBridges )
2021-03-08 15:41:13 +00:00
{
//We need to match the adapter index to an INetCfgComponent
//We do this by matching IP_ADAPTER_ADDRESSES.AdapterName
//with the INetCfgComponent Instance GUID
2024-05-19 18:06:07 +01:00
PIP_ADAPTER_ADDRESSES cAdapterInfo = FindAdapterViaIndex ( pAdapterFirst , index );
2021-03-08 15:41:13 +00:00
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
2021-07-07 22:22:31 +02:00
wil :: com_ptr_nothrow < INetCfgComponent > component ;
2021-03-08 15:41:13 +00:00
while ( true )
{
2021-07-07 22:22:31 +02:00
if ( components -> Next ( 1 , component . put (), nullptr ) != S_OK )
2021-03-08 15:41:13 +00:00
break ;
GUID comInstGuid ;
hr = component -> GetInstanceGuid ( & comInstGuid );
if ( SUCCEEDED ( hr ) && IsEqualGUID ( nameGuid , comInstGuid ))
{
2021-07-07 22:22:31 +02:00
wil :: unique_cotaskmem_string comId ;
hr = component -> GetId ( comId . put ());
2021-03-08 15:41:13 +00:00
if ( ! SUCCEEDED ( hr ))
continue ;
//The bridge adapter for Win8+ has this ComponentID
//However not every adapter with this componentID is a bridge
2021-07-07 22:22:31 +02:00
if ( wcscmp ( L "compositebus \\ ms_implat_mp" , comId . get ()) == 0 )
2021-03-08 15:41:13 +00:00
{
2021-07-07 22:22:31 +02:00
wil :: unique_cotaskmem_string dispName ;
hr = component -> GetDisplayName ( dispName . put ());
2021-03-08 15:41:13 +00:00
if ( SUCCEEDED ( hr ))
2022-05-18 23:27:23 +10:00
Console . WriteLn ( fmt :: format ( "DEV9: {} is possible bridge (Check 2 passed)" , StringUtil :: WideStringToUTF8String ( dispName . get ())));
2021-03-08 15:41:13 +00:00
//Check if adapter has the ms_bridge component bound to it.
2021-07-07 22:22:31 +02:00
auto bindings = bridge . try_query < INetCfgComponentBindings > ();
if ( ! bindings )
2021-03-08 15:41:13 +00:00
continue ;
2021-07-07 22:22:31 +02:00
hr = bindings -> IsBoundTo ( component . get ());
2021-03-08 15:41:13 +00:00
if ( hr != S_OK )
continue ;
2021-07-07 22:22:31 +02:00
hr = component -> GetDisplayName ( dispName . put ());
2021-03-08 15:41:13 +00:00
if ( SUCCEEDED ( hr ))
2022-05-18 23:27:23 +10:00
Console . WriteLn ( fmt :: format ( "DEV9: {} is bridge (Check 3 passed)" , StringUtil :: WideStringToUTF8String ( dispName . get ())));
2021-03-08 15:41:13 +00:00
bridgeAdapter = cAdapterInfo ;
break ;
}
}
}
components -> Reset ();
if ( bridgeAdapter != nullptr )
break ;
}
}
}
netcfg -> Uninitialize ();
}
}
if ( bridgeAdapter != nullptr )
{
* adapter = * bridgeAdapter ;
2024-05-19 18:06:07 +01:00
buffer -> swap ( adapterInfo );
2021-03-08 15:41:13 +00:00
return true ;
}
return false ;
}
2013-03-19 22:01:41 +00:00
TAPAdapter :: TAPAdapter ()
2020-12-29 20:16:41 +00:00
: NetAdapter ()
2013-03-19 22:01:41 +00:00
{
2022-02-07 23:38:50 +00:00
if ( ! EmuConfig . DEV9 . EthEnable )
2020-09-22 21:00:10 +02:00
return ;
2022-02-07 23:38:50 +00:00
htap = TAPOpen ( EmuConfig . DEV9 . EthDevice );
2013-03-19 22:01:41 +00:00
2020-10-03 19:34:31 +02:00
read . Offset = 0 ;
read . OffsetHigh = 0 ;
read . hEvent = CreateEvent ( NULL , FALSE , FALSE , NULL );
2013-03-19 22:01:41 +00:00
2020-10-03 21:33:15 +02:00
write . Offset = 0 ;
write . OffsetHigh = 0 ;
write . hEvent = CreateEvent ( NULL , FALSE , FALSE , NULL );
2020-12-18 20:53:21 +00:00
cancel = CreateEvent ( NULL , TRUE , FALSE , NULL );
2023-01-16 20:34:19 +00:00
PacketReader :: MAC_Address hostMAC ;
PacketReader :: MAC_Address newMAC ;
2020-12-29 22:57:54 +00:00
2023-01-16 20:34:19 +00:00
TAPGetMACAddress ( htap , & hostMAC );
newMAC = ps2MAC ;
2020-12-29 22:57:54 +00:00
//Lets take the hosts last 2 bytes to make it unique on Xlink
2023-01-16 20:34:19 +00:00
newMAC . bytes [ 5 ] = hostMAC . bytes [ 4 ];
newMAC . bytes [ 4 ] = hostMAC . bytes [ 5 ];
2020-12-29 22:57:54 +00:00
2023-01-16 20:34:19 +00:00
SetMACAddress ( & newMAC );
2020-12-29 22:57:54 +00:00
2021-03-08 15:41:13 +00:00
IP_ADAPTER_ADDRESSES adapter ;
2024-05-19 18:06:07 +01:00
AdapterUtils :: AdapterBuffer buffer ;
2022-02-07 23:38:50 +00:00
if ( TAPGetWin32Adapter ( EmuConfig . DEV9 . EthDevice , & adapter , & buffer ))
2021-03-08 15:41:13 +00:00
InitInternalServer ( & adapter );
else
2021-09-26 15:59:40 +01:00
{
Console . Error ( "DEV9: Failed to get adapter information" );
2021-03-08 15:41:13 +00:00
InitInternalServer ( nullptr );
2021-09-26 15:59:40 +01:00
}
2021-03-08 15:41:13 +00:00
2020-10-03 21:33:15 +02:00
isActive = true ;
2013-03-19 22:01:41 +00:00
}
bool TAPAdapter :: blocks ()
{
2020-10-03 19:34:31 +02:00
return true ; //we use blocking io
2013-03-19 22:01:41 +00:00
}
2015-11-14 14:25:34 +00:00
bool TAPAdapter :: isInitialised ()
{
return ( htap != NULL );
}
2013-03-19 22:01:41 +00:00
//gets a packet.rv :true success
bool TAPAdapter :: recv ( NetPacket * pkt )
{
DWORD read_size ;
BOOL result = ReadFile ( htap ,
2021-09-26 16:38:49 +01:00
pkt -> buffer ,
sizeof ( pkt -> buffer ),
& read_size ,
& read );
2013-03-19 22:01:41 +00:00
2020-10-03 19:34:31 +02:00
if ( ! result )
{
2023-06-16 20:36:18 +02:00
const DWORD dwError = GetLastError ();
2020-10-03 19:34:31 +02:00
if ( dwError == ERROR_IO_PENDING )
{
2020-12-18 20:53:21 +00:00
HANDLE readHandles []{ read . hEvent , cancel };
const DWORD waitResult = WaitForMultipleObjects ( 2 , readHandles , FALSE , INFINITE );
if ( waitResult == WAIT_OBJECT_0 + 1 )
{
CancelIo ( htap );
//Wait for the I/O subsystem to acknowledge our cancellation
2020-12-19 02:59:14 +00:00
result = GetOverlappedResult ( htap , & read , & read_size , TRUE );
2020-12-18 20:53:21 +00:00
}
else
2020-12-19 02:59:14 +00:00
result = GetOverlappedResult ( htap , & read , & read_size , FALSE );
2020-10-03 19:34:31 +02:00
}
}
2013-03-19 22:01:41 +00:00
2021-02-04 13:26:56 +00:00
if ( result && VerifyPkt ( pkt , read_size ))
{
InspectRecv ( pkt );
return true ;
}
2013-03-19 22:01:41 +00:00
else
return false ;
}
//sends the packet .rv :true success
bool TAPAdapter :: send ( NetPacket * pkt )
{
2021-02-04 13:26:56 +00:00
InspectSend ( pkt );
2021-03-08 15:41:13 +00:00
if ( NetAdapter :: send ( pkt ))
return true ;
2013-03-19 22:01:41 +00:00
DWORD writen ;
BOOL result = WriteFile ( htap ,
2021-09-26 16:38:49 +01:00
pkt -> buffer ,
pkt -> size ,
& writen ,
& write );
2013-03-19 22:01:41 +00:00
2020-10-03 19:34:31 +02:00
if ( ! result )
{
2023-06-16 20:36:18 +02:00
const DWORD dwError = GetLastError ();
2020-10-03 19:34:31 +02:00
if ( dwError == ERROR_IO_PENDING )
{
WaitForSingleObject ( write . hEvent , INFINITE );
2020-12-19 02:59:14 +00:00
result = GetOverlappedResult ( htap , & write , & writen , FALSE );
2020-10-03 19:34:31 +02:00
}
}
2013-03-19 22:01:41 +00:00
if ( result )
{
2020-10-03 19:34:31 +02:00
if ( writen != pkt -> size )
2013-03-19 22:01:41 +00:00
return false ;
return true ;
}
else
return false ;
}
2021-03-18 23:05:35 +00:00
void TAPAdapter :: reloadSettings ()
{
IP_ADAPTER_ADDRESSES adapter ;
2024-05-19 18:06:07 +01:00
AdapterUtils :: AdapterBuffer buffer ;
2022-02-07 23:38:50 +00:00
if ( TAPGetWin32Adapter ( EmuConfig . DEV9 . EthDevice , & adapter , & buffer ))
2021-03-18 23:05:35 +00:00
ReloadInternalServer ( & adapter );
else
ReloadInternalServer ( nullptr );
}
2020-12-18 20:53:21 +00:00
void TAPAdapter :: close ()
{
SetEvent ( cancel );
}
2013-03-19 22:01:41 +00:00
TAPAdapter ::~ TAPAdapter ()
{
2020-10-03 21:55:40 +02:00
if ( ! isActive )
return ;
2013-03-19 22:01:41 +00:00
CloseHandle ( read . hEvent );
CloseHandle ( write . hEvent );
2020-12-18 20:53:21 +00:00
CloseHandle ( cancel );
2013-03-19 22:01:41 +00:00
TAPSetStatus ( htap , FALSE );
CloseHandle ( htap );
2020-10-03 21:55:40 +02:00
isActive = false ;
2020-10-29 10:27:54 +01:00
}