I think I've addressed all the issues that were mentioned.
I've moved the test from iphlpapi to ndis.sys. I think I'm using the LoadOrderGroup correctly and it doesn't create any extra processes.
I also removed the creation of the symlink with DefineDosDevice. It was inspired by some code I reversed in a third party dll, which the patches are trying to address.
v1: https://www.winehq.org/pipermail/wine-devel/2020-August/172383.html
Isabella Bosia (7): iphlpapi: Unify conversions to interface guid. iphlpapi: Make the interface guids more unique. wine.inf: Create NDIS service. ndis.sys: Create network card registry keys. ndis.sys: Create network card devices. ndis.sys: Implement IOCTL_NDIS_QUERY_GLOBAL_STATS on network cards. ndis.sys/tests: Add tests for ndis ioctls.
configure.ac | 1 + dlls/iphlpapi/iphlpapi_main.c | 12 ++- dlls/ndis.sys/Makefile.in | 1 + dlls/ndis.sys/main.c | 159 ++++++++++++++++++++++++++++++++ dlls/ndis.sys/tests/Makefile.in | 5 + dlls/ndis.sys/tests/ndis.c | 146 +++++++++++++++++++++++++++++ loader/wine.inf.in | 14 +++ 7 files changed, 336 insertions(+), 2 deletions(-) create mode 100644 dlls/ndis.sys/tests/Makefile.in create mode 100644 dlls/ndis.sys/tests/ndis.c
Signed-off-by: Isabella Bosia ibosia@codeweavers.com --- dlls/iphlpapi/iphlpapi_main.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/dlls/iphlpapi/iphlpapi_main.c b/dlls/iphlpapi/iphlpapi_main.c index c039086229..ddde081950 100644 --- a/dlls/iphlpapi/iphlpapi_main.c +++ b/dlls/iphlpapi/iphlpapi_main.c @@ -1008,12 +1008,19 @@ static ULONG adapterAddressesFromIndex(ULONG family, ULONG flags, IF_INDEX index WCHAR *dst; DWORD buflen, type; INTERNAL_IF_OPER_STATUS status; + NET_LUID luid; + GUID guid;
memset(aa, 0, sizeof(IP_ADAPTER_ADDRESSES)); aa->u.s.Length = sizeof(IP_ADAPTER_ADDRESSES); aa->u.s.IfIndex = index;
- sprintf(ptr, "{%08x-0000-0000-0000-000000000000}", index); + ConvertInterfaceIndexToLuid(index, &luid); + ConvertInterfaceLuidToGuid(&luid, &guid); + sprintf(ptr, "{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}", + guid.Data1, guid.Data2, guid.Data3, guid.Data4[0], guid.Data4[1], + guid.Data4[2], guid.Data4[3], guid.Data4[4], guid.Data4[5], + guid.Data4[6], guid.Data4[7]); aa->AdapterName = ptr; ptr += 39;
@@ -1786,7 +1793,7 @@ DWORD WINAPI GetIfEntry2( MIB_IF_ROW2 *row2 ) row2->InterfaceLuid.Info.NetLuidIndex = row.dwIndex; row2->InterfaceLuid.Info.IfType = row.dwType; row2->InterfaceIndex = row.dwIndex; - row2->InterfaceGuid.Data1 = row.dwIndex; + ConvertInterfaceLuidToGuid( &row2->InterfaceLuid, &row2->InterfaceGuid ); row2->Type = row.dwType; row2->Mtu = row.dwMtu; MultiByteToWideChar( CP_UNIXCP, 0, (const char *)row.bDescr, -1, row2->Description, len );
Signed-off-by: Isabella Bosia ibosia@codeweavers.com --- dlls/iphlpapi/iphlpapi_main.c | 1 + 1 file changed, 1 insertion(+)
diff --git a/dlls/iphlpapi/iphlpapi_main.c b/dlls/iphlpapi/iphlpapi_main.c index ddde081950..81c4992ac7 100644 --- a/dlls/iphlpapi/iphlpapi_main.c +++ b/dlls/iphlpapi/iphlpapi_main.c @@ -3207,6 +3207,7 @@ DWORD WINAPI ConvertInterfaceLuidToGuid(const NET_LUID *luid, GUID *guid)
memset( guid, 0, sizeof(*guid) ); guid->Data1 = luid->Info.NetLuidIndex; + memcpy( guid->Data4+2, "NetDev", 6 ); return NO_ERROR; }
Signed-off-by: Isabella Bosia ibosia@codeweavers.com --- loader/wine.inf.in | 14 ++++++++++++++ 1 file changed, 14 insertions(+)
diff --git a/loader/wine.inf.in b/loader/wine.inf.in index 8777377002..cd0e7bd16f 100644 --- a/loader/wine.inf.in +++ b/loader/wine.inf.in @@ -163,6 +163,7 @@ AddService=FontCache,0,FontCacheService AddService=Schedule,0,TaskSchedulerService AddService=Winmgmt,0,WinmgmtService AddService=wuauserv,0,wuauService +AddService=NDIS,0x800,NDISService
[DefaultInstall.NT.Services] AddService=BITS,0,BITSService @@ -180,6 +181,7 @@ AddService=FontCache,0,FontCacheService AddService=Schedule,0,TaskSchedulerService AddService=Winmgmt,0,WinmgmtService AddService=wuauserv,0,wuauService +AddService=NDIS,0x800,NDISService
[DefaultInstall.ntamd64.Services] AddService=BITS,0,BITSService @@ -197,6 +199,7 @@ AddService=FontCache,0,FontCacheService AddService=Schedule,0,TaskSchedulerService AddService=Winmgmt,0,WinmgmtService AddService=wuauserv,0,wuauService +AddService=NDIS,0x800,NDISService
[DefaultInstall.ntarm64.Services] AddService=BITS,0,BITSService @@ -214,6 +217,7 @@ AddService=FontCache,0,FontCacheService AddService=Schedule,0,TaskSchedulerService AddService=Winmgmt,0,WinmgmtService AddService=wuauserv,0,wuauService +AddService=NDIS,0x800,NDISService
[Strings] MciExtStr="Software\Microsoft\Windows NT\CurrentVersion\MCI Extensions" @@ -3628,6 +3632,16 @@ ServiceBinary="%12%\mountmgr.sys" ServiceType=1 StartType=2 ErrorControl=1 +LoadOrderGroup="System Bus Extender" + +[NDISService] +Description="NDIS service" +DisplayName="NDIS" +ServiceBinary="%12%\ndis.sys" +ServiceType=1 +StartType=2 +ErrorControl=1 +LoadOrderGroup="System Bus Extender"
[RpcSsService] Description="RPC service"
Signed-off-by: Isabella Bosia ibosia@codeweavers.com --- dlls/ndis.sys/Makefile.in | 1 + dlls/ndis.sys/main.c | 50 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+)
diff --git a/dlls/ndis.sys/Makefile.in b/dlls/ndis.sys/Makefile.in index fa69910f16..0d58cb0d38 100644 --- a/dlls/ndis.sys/Makefile.in +++ b/dlls/ndis.sys/Makefile.in @@ -1,4 +1,5 @@ MODULE = ndis.sys +IMPORTS = advapi32 ntoskrnl iphlpapi EXTRADLLFLAGS = -Wl,--subsystem,native -mno-cygwin
C_SRCS = \ diff --git a/dlls/ndis.sys/main.c b/dlls/ndis.sys/main.c index 90c8c3cc88..6dd444a657 100644 --- a/dlls/ndis.sys/main.c +++ b/dlls/ndis.sys/main.c @@ -21,21 +21,71 @@
#include <stdarg.h>
+#define NONAMELESSUNION #include "ntstatus.h" #define WIN32_NO_STATUS #include "windef.h" #include "winbase.h" #include "winternl.h" +#include "winioctl.h" +#include "ntddndis.h" #include "ddk/wdm.h" #include "ddk/ndis.h" +#include "winreg.h" #include "wine/debug.h"
+#include <winsock2.h> +#include <ws2ipdef.h> +#include <iphlpapi.h> +#include <netioapi.h> + WINE_DEFAULT_DEBUG_CHANNEL(ndis);
+static void add_key(const WCHAR *guidstrW, const MIB_IF_ROW2 *netdev) +{ + HKEY card_key; + WCHAR keynameW[100]; + + swprintf( keynameW, ARRAY_SIZE(keynameW), L"Software\Microsoft\Windows NT\CurrentVersion\NetworkCards\%d", netdev->InterfaceIndex ); + if (RegCreateKeyExW( HKEY_LOCAL_MACHINE, keynameW, 0, NULL, + REG_OPTION_VOLATILE, KEY_ALL_ACCESS, NULL, &card_key, NULL ) == ERROR_SUCCESS) + { + RegSetValueExW( card_key, L"Description", 0, REG_SZ, (BYTE *)netdev->Description, (lstrlenW(netdev->Description) + 1) * sizeof(WCHAR) ); + RegSetValueExW( card_key, L"ServiceName", 0, REG_SZ, (BYTE *)guidstrW, (lstrlenW(guidstrW) + 1) * sizeof(WCHAR) ); + RegCloseKey( card_key ); + } +} + +static void create_network_devices(DRIVER_OBJECT *driver) +{ + MIB_IF_TABLE2 *table; + ULONG i; + + if (GetIfTable2( &table ) != NO_ERROR) + return; + + for (i = 0; i < table->NumEntries; i++) + { + GUID *guid = &table->Table[i].InterfaceGuid; + WCHAR guidstrW[39]; + + swprintf( guidstrW, ARRAY_SIZE(guidstrW), L"{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}", + guid->Data1, guid->Data2, guid->Data3, guid->Data4[0], guid->Data4[1], + guid->Data4[2], guid->Data4[3], guid->Data4[4], guid->Data4[5], + guid->Data4[6], guid->Data4[7] ); + + add_key( guidstrW, &table->Table[i] ); + } + + FreeMibTable( table ); +} + NTSTATUS WINAPI DriverEntry(DRIVER_OBJECT *driver, UNICODE_STRING *path) { TRACE("(%p, %s)\n", driver, debugstr_w(path->Buffer));
+ create_network_devices( driver ); + return STATUS_SUCCESS; }
Signed-off-by: Isabella Bosia ibosia@codeweavers.com --- dlls/ndis.sys/main.c | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-)
diff --git a/dlls/ndis.sys/main.c b/dlls/ndis.sys/main.c index 6dd444a657..7eb0c23a53 100644 --- a/dlls/ndis.sys/main.c +++ b/dlls/ndis.sys/main.c @@ -56,6 +56,32 @@ static void add_key(const WCHAR *guidstrW, const MIB_IF_ROW2 *netdev) } }
+static int add_device(DRIVER_OBJECT *driver, const WCHAR *guidstrW, MIB_IF_ROW2 *netdev) +{ + WCHAR nameW[47], linkW[51]; + UNICODE_STRING name, link; + DEVICE_OBJECT *device; + NTSTATUS status; + + swprintf( nameW, ARRAY_SIZE(nameW), L"\Device\%s", guidstrW ); + RtlInitUnicodeString( &name, nameW ); + + swprintf( linkW, ARRAY_SIZE(linkW), L"\DosDevices\%s", guidstrW ); + RtlInitUnicodeString( &link, linkW ); + + if (!(status = IoCreateDevice( driver, sizeof(*netdev), &name, 0, 0, FALSE, &device ))) + status = IoCreateSymbolicLink( &link, &name ); + if (status) + { + FIXME( "failed to create device error %x\n", status ); + return 0; + } + + memcpy( device->DeviceExtension, netdev, sizeof(*netdev) ); + return 1; +} + + static void create_network_devices(DRIVER_OBJECT *driver) { MIB_IF_TABLE2 *table; @@ -74,7 +100,8 @@ static void create_network_devices(DRIVER_OBJECT *driver) guid->Data4[2], guid->Data4[3], guid->Data4[4], guid->Data4[5], guid->Data4[6], guid->Data4[7] );
- add_key( guidstrW, &table->Table[i] ); + if (add_device( driver, guidstrW, &table->Table[i] )) + add_key( guidstrW, &table->Table[i] ); }
FreeMibTable( table );
Signed-off-by: Isabella Bosia ibosia@codeweavers.com --- dlls/ndis.sys/main.c | 82 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+)
diff --git a/dlls/ndis.sys/main.c b/dlls/ndis.sys/main.c index 7eb0c23a53..02f6c2417a 100644 --- a/dlls/ndis.sys/main.c +++ b/dlls/ndis.sys/main.c @@ -41,6 +41,86 @@
WINE_DEFAULT_DEBUG_CHANNEL(ndis);
+static void query_global_stats(IRP *irp, const MIB_IF_ROW2 *netdev) +{ + IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation( irp ); + void *response = MmGetSystemAddressForMdlSafe( irp->MdlAddress, NormalPagePriority ); + DWORD len = irpsp->Parameters.DeviceIoControl.OutputBufferLength; + DWORD oid; + + if (irpsp->Parameters.DeviceIoControl.InputBufferLength != sizeof(oid)) + { + irp->IoStatus.u.Status = STATUS_INVALID_PARAMETER; + return; + } + oid = *(DWORD *)irp->AssociatedIrp.SystemBuffer; + + switch (oid) + { + case OID_GEN_MEDIA_SUPPORTED: + case OID_GEN_MEDIA_IN_USE: + { + if (len < sizeof(NDIS_MEDIUM)) + { + irp->IoStatus.u.Status = STATUS_INVALID_PARAMETER; + break; + } + *(NDIS_MEDIUM *)response = netdev->MediaType; + irp->IoStatus.Information = sizeof(netdev->MediaType); + irp->IoStatus.u.Status = STATUS_SUCCESS; + break; + } + case OID_802_3_PERMANENT_ADDRESS: + { + irp->IoStatus.Information = netdev->PhysicalAddressLength; + if (len < netdev->PhysicalAddressLength) + irp->IoStatus.u.Status = STATUS_INVALID_PARAMETER; + else + memcpy( response, netdev->PermanentPhysicalAddress, sizeof(netdev->PermanentPhysicalAddress) ); + break; + } + case OID_802_3_CURRENT_ADDRESS: + { + irp->IoStatus.Information = netdev->PhysicalAddressLength; + if (len < netdev->PhysicalAddressLength) + irp->IoStatus.u.Status = STATUS_INVALID_PARAMETER; + else + memcpy( response, netdev->PhysicalAddress, sizeof(netdev->PhysicalAddress) ); + break; + + } + default: + FIXME( "Unsupported OID %x\n", oid ); + irp->IoStatus.u.Status = STATUS_NOT_SUPPORTED; + break; + } +} + +static NTSTATUS WINAPI ndis_ioctl(DEVICE_OBJECT *device, IRP *irp) +{ + IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation( irp ); + MIB_IF_ROW2 *netdev = device->DeviceExtension; + + TRACE( "ioctl %x insize %u outsize %u\n", + irpsp->Parameters.DeviceIoControl.IoControlCode, + irpsp->Parameters.DeviceIoControl.InputBufferLength, + irpsp->Parameters.DeviceIoControl.OutputBufferLength ); + + switch (irpsp->Parameters.DeviceIoControl.IoControlCode) + { + case IOCTL_NDIS_QUERY_GLOBAL_STATS: + query_global_stats(irp, netdev); + break; + default: + FIXME( "ioctl %x not supported\n", irpsp->Parameters.DeviceIoControl.IoControlCode ); + irp->IoStatus.u.Status = STATUS_NOT_SUPPORTED; + break; + } + + IoCompleteRequest( irp, IO_NO_INCREMENT ); + return STATUS_SUCCESS; +} + static void add_key(const WCHAR *guidstrW, const MIB_IF_ROW2 *netdev) { HKEY card_key; @@ -111,6 +191,8 @@ NTSTATUS WINAPI DriverEntry(DRIVER_OBJECT *driver, UNICODE_STRING *path) { TRACE("(%p, %s)\n", driver, debugstr_w(path->Buffer));
+ driver->MajorFunction[IRP_MJ_DEVICE_CONTROL] = ndis_ioctl; + create_network_devices( driver );
return STATUS_SUCCESS;
Signed-off-by: Isabella Bosia ibosia@codeweavers.com --- configure.ac | 1 + dlls/ndis.sys/tests/Makefile.in | 5 ++ dlls/ndis.sys/tests/ndis.c | 146 ++++++++++++++++++++++++++++++++ 3 files changed, 152 insertions(+) create mode 100644 dlls/ndis.sys/tests/Makefile.in create mode 100644 dlls/ndis.sys/tests/ndis.c
diff --git a/configure.ac b/configure.ac index 13adf2da7e..f60cd59354 100644 --- a/configure.ac +++ b/configure.ac @@ -3579,6 +3579,7 @@ WINE_CONFIG_MAKEFILE(dlls/mtxdm) WINE_CONFIG_MAKEFILE(dlls/ncrypt) WINE_CONFIG_MAKEFILE(dlls/nddeapi) WINE_CONFIG_MAKEFILE(dlls/ndis.sys) +WINE_CONFIG_MAKEFILE(dlls/ndis.sys/tests) WINE_CONFIG_MAKEFILE(dlls/netapi32) WINE_CONFIG_MAKEFILE(dlls/netapi32/tests) WINE_CONFIG_MAKEFILE(dlls/netcfgx) diff --git a/dlls/ndis.sys/tests/Makefile.in b/dlls/ndis.sys/tests/Makefile.in new file mode 100644 index 0000000000..8544dfdff2 --- /dev/null +++ b/dlls/ndis.sys/tests/Makefile.in @@ -0,0 +1,5 @@ +TESTDLL = ndis.sys +IMPORTS = iphlpapi advapi32 ole32 + +C_SRCS = \ + ndis.c diff --git a/dlls/ndis.sys/tests/ndis.c b/dlls/ndis.sys/tests/ndis.c new file mode 100644 index 0000000000..821eb3d55b --- /dev/null +++ b/dlls/ndis.sys/tests/ndis.c @@ -0,0 +1,146 @@ +/* + * Unit tests for ndis ioctls + * + * Copyright (c) 2020 Isabella Bosia + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <ntstatus.h> +#define WIN32_NO_STATUS +#include <windows.h> +#include <winioctl.h> +#include <ntddndis.h> +#include <winsock2.h> +#include <ws2ipdef.h> +#include <iphlpapi.h> +#include <netioapi.h> +#include <shlwapi.h> +#include <stdio.h> +#include <string.h> +#include <winternl.h> +#include <winnt.h> +#include "wine/test.h" + +static void test_device(const WCHAR *service_name, const MIB_IF_ROW2 *row) +{ + DWORD size; + int ret; + NDIS_MEDIUM medium; + UCHAR addr[IF_MAX_PHYS_ADDRESS_LENGTH]; + HANDLE netdev = INVALID_HANDLE_VALUE; + int oid; + UNICODE_STRING str; + OBJECT_ATTRIBUTES attr; + NTSTATUS status; + IO_STATUS_BLOCK iosb; + WCHAR meoW[47]; + + swprintf( meoW, ARRAY_SIZE(meoW), L"\Device\%s", service_name ); + RtlInitUnicodeString( &str, meoW ); + InitializeObjectAttributes( &attr, &str, 0, 0, NULL ); + status = NtOpenFile( &netdev, GENERIC_READ, &attr, &iosb, FILE_SHARE_READ, 0 ); + + if (status != STATUS_SUCCESS) + { + skip( "Couldn't open the device (status = %d)\n", status ); + return; + } + + oid = OID_GEN_MEDIA_SUPPORTED; + ret = DeviceIoControl( netdev, IOCTL_NDIS_QUERY_GLOBAL_STATS, + &oid, sizeof(oid), &medium, sizeof(medium), &size, NULL ); + ok( ret, "OID_GEN_MEDIA_SUPPORTED failed (ret = %d)\n", ret ); + ok( medium == row->MediaType, "Wrong media type\n" ); + + oid = OID_GEN_MEDIA_IN_USE; + ret = DeviceIoControl( netdev, IOCTL_NDIS_QUERY_GLOBAL_STATS, + &oid, sizeof(oid), &medium, sizeof(medium), &size, NULL ); + ok( ret, "OID_GEN_MEDIA_IN_USE failed (ret = %d)\n", ret ); + ok( medium == row->MediaType, "Wrong media type\n" ); + + oid = OID_802_3_PERMANENT_ADDRESS; + ret = DeviceIoControl( netdev, IOCTL_NDIS_QUERY_GLOBAL_STATS, + &oid, sizeof(oid), addr, sizeof(addr), &size, NULL ); + ok( ret, "OID_802_3_PERMANENT_ADDRESS failed (ret = %d)\n", ret ); + ok( row->PhysicalAddressLength == size && !memcmp( row->PermanentPhysicalAddress, addr, size ), + "Wrong permanent address\n" ); + + oid = OID_802_3_CURRENT_ADDRESS; + ret = DeviceIoControl( netdev, IOCTL_NDIS_QUERY_GLOBAL_STATS, + &oid, sizeof(oid), addr, sizeof(addr), &size, NULL ); + ok( ret, "OID_802_3_CURRENT_ADDRESS failed (ret = %d)\n", ret ); + ok( row->PhysicalAddressLength == size && !memcmp( row->PhysicalAddress, addr, size ), + "Wrong current address\n" ); +} + +static void test_ndis_ioctl(void) +{ + HKEY nics, sub_key; + LSTATUS ret; + WCHAR card[16]; + WCHAR description[100], service_name[100]; + DWORD size, type, i = 0; + + ret = RegOpenKeyExW( HKEY_LOCAL_MACHINE, + L"Software\Microsoft\Windows NT\CurrentVersion\NetworkCards", 0, KEY_READ, &nics ); + ok( ret == ERROR_SUCCESS, "NetworkCards key missing\n" ); + + while (1) + { + MIB_IF_ROW2 row = { 0 }; + GUID guid; + + size = sizeof(card); + ret = RegEnumKeyExW( nics, i, card, &size, NULL, NULL, NULL, NULL ); + if (ret != ERROR_SUCCESS) + break; + i++; + + ret = RegOpenKeyExW( nics, card, 0, KEY_READ, &sub_key ); + ok( ret == ERROR_SUCCESS, "Could not open network card subkey\n" ); + + size = sizeof(service_name); + ret = RegQueryValueExW( sub_key, L"ServiceName", NULL, &type, (BYTE *)service_name, &size ); + ok( ret == ERROR_SUCCESS && type == REG_SZ, "Wrong ServiceName\n" ); + + CLSIDFromString( service_name, (LPCLSID)&guid ); + ConvertInterfaceGuidToLuid( &guid, &row.InterfaceLuid ); + + ret = GetIfEntry2(&row); + ok( ret == NO_ERROR, "GetIfEntry2 failed\n" ); + + ok( IsEqualGUID( &guid, &row.InterfaceGuid ), "Wrong ServiceName\n" ); + + size = sizeof(description); + ret = RegQueryValueExW( sub_key, L"Description", NULL, &type, (BYTE *)description, &size ); + ok( ret == ERROR_SUCCESS && type == REG_SZ, "Wrong Description\n" ); + + trace( "testing device <%s>\n", wine_dbgstr_w(description) ); + test_device( service_name, &row ); + + RegCloseKey( sub_key ); + } + + if (i == 0) + skip( "Network card subkeys missing\n" ); + + RegCloseKey( nics ); +} + +START_TEST(ndis) +{ + test_ndis_ioctl(); +}