Signed-off-by: Huw Davies huw@codeweavers.com --- dlls/nsi/tests/Makefile.in | 5 ++ dlls/nsi/tests/nsi.c | 146 +++++++++++++++++++++++++++++++++++++ include/wine/nsi.h | 73 +++++++++++++++++++ 3 files changed, 224 insertions(+) create mode 100644 dlls/nsi/tests/Makefile.in create mode 100644 dlls/nsi/tests/nsi.c
diff --git a/dlls/nsi/tests/Makefile.in b/dlls/nsi/tests/Makefile.in new file mode 100644 index 00000000000..98129935198 --- /dev/null +++ b/dlls/nsi/tests/Makefile.in @@ -0,0 +1,5 @@ +TESTDLL = nsi.dll +IMPORTS = nsi uuid iphlpapi + +C_SRCS = \ + nsi.c diff --git a/dlls/nsi/tests/nsi.c b/dlls/nsi/tests/nsi.c new file mode 100644 index 00000000000..2fc1876c833 --- /dev/null +++ b/dlls/nsi/tests/nsi.c @@ -0,0 +1,146 @@ +/* + * Network Store Interface tests + * + * Copyright 2021 Huw Davies + * + * 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 "winsock2.h" +#include "winternl.h" +#include "ws2ipdef.h" +#include "iphlpapi.h" +#include "netioapi.h" +#include "iptypes.h" +#include "netiodef.h" +#include "wine/nsi.h" +#include "wine/test.h" + +static int bounded( ULONG64 val, ULONG64 lo, ULONG64 hi ) +{ + return lo <= val && val <= hi; +} + +static void test_ndis_ifinfo( void ) +{ + DWORD rw_sizes[] = { FIELD_OFFSET(struct nsi_ndis_ifinfo_rw, name2), FIELD_OFFSET(struct nsi_ndis_ifinfo_rw, unk), + sizeof(struct nsi_ndis_ifinfo_rw) }; + struct nsi_ndis_ifinfo_rw *rw_tbl; + struct nsi_ndis_ifinfo_dynamic *dyn_tbl, *dyn_tbl_2; + struct nsi_ndis_ifinfo_static *stat_tbl; + DWORD err, count, i, rw_size; + NET_LUID *luid_tbl, *luid_tbl_2; + MIB_IF_TABLE2 *table; + + /* Contents of GetIfTable2() keyed by luids */ + + for (i = 0; i < ARRAY_SIZE(rw_sizes); i++) + { + err = NsiAllocateAndGetTable( 1, &NPI_MS_NDIS_MODULEID, NSI_NDIS_IFINFO_TABLE, (void **)&luid_tbl, sizeof(*luid_tbl), + (void **)&rw_tbl, rw_sizes[i], (void **)&dyn_tbl, sizeof(*dyn_tbl), + (void **)&stat_tbl, sizeof(*stat_tbl), &count, 0 ); + if (!err) break; + } +todo_wine + ok( !err, "got %d\n", err ); + if (err) return; + rw_size = rw_sizes[i]; + + err = GetIfTable2( &table ); + ok( !err, "got %d\n", err ); + ok( table->NumEntries == count, "table entries %d count %d\n", table->NumEntries, count ); + + /* Grab the dyn table again to provide an upper bound for the stats returned from GetIfTable2(). + (The luids must be retrieved again otherwise NsiAllocateAndGetTable() fails). */ + err = NsiAllocateAndGetTable( 1, &NPI_MS_NDIS_MODULEID, NSI_NDIS_IFINFO_TABLE, (void **)&luid_tbl_2, sizeof(*luid_tbl_2), + NULL, 0, (void **)&dyn_tbl_2, sizeof(*dyn_tbl_2), + NULL, 0, &count, 0 ); + ok( !err, "got %d\n", err ); + ok( table->NumEntries == count, "table entries %d count %d\n", table->NumEntries, count ); + + for (i = 0; i < count; i++) + { + MIB_IF_ROW2 *row = table->Table + i; + NET_LUID *luid = luid_tbl + i; + struct nsi_ndis_ifinfo_rw *rw = (struct nsi_ndis_ifinfo_rw *)((BYTE *)rw_tbl + i * rw_size); + struct nsi_ndis_ifinfo_dynamic *dyn = dyn_tbl + i, *dyn_2 = dyn_tbl_2 + i; + struct nsi_ndis_ifinfo_static *stat = stat_tbl + i; + + ok( row->InterfaceLuid.Value == luid->Value, "%d: mismatch\n", i ); + ok( row->InterfaceIndex == stat->if_index, "%d: mismatch\n", i ); + ok( IsEqualGUID( &row->InterfaceGuid, &stat->if_guid ), "%d: mismatch\n", i ); + ok( !memcmp( row->Alias, rw->alias.String, rw->alias.Length ), "%d: mismatch\n", i ); + ok( lstrlenW( row->Alias ) * 2 == rw->alias.Length, "%d: mismatch\n", i ); + ok( !memcmp( row->Description, stat->descr.String, sizeof(row->Description) ), "%d: mismatch\n", i ); + ok( lstrlenW( row->Description ) * 2 == stat->descr.Length, "%d: mismatch\n", i ); + ok( row->PhysicalAddressLength == rw->phys_addr.Length, "%d: mismatch\n", i ); + ok( !memcmp( row->PhysicalAddress, rw->phys_addr.Address, IF_MAX_PHYS_ADDRESS_LENGTH ), "%d: mismatch\n", i ); + ok( row->PhysicalAddressLength == stat->perm_phys_addr.Length, "%d: mismatch\n", i ); + ok( !memcmp( row->PermanentPhysicalAddress, stat->perm_phys_addr.Address, IF_MAX_PHYS_ADDRESS_LENGTH ), "%d: mismatch\n", i ); + ok( row->Mtu == dyn->mtu, "%d: mismatch\n", i ); + ok( row->Type == stat->type, "%d: mismatch\n", i ); + /* TunnelType */ + ok( row->MediaType == stat->media_type, "%d: mismatch\n", i ); + ok( row->PhysicalMediumType == stat->phys_medium_type, "%d: mismatch\n", i ); + ok( row->AccessType == stat->access_type, "%d: mismatch\n", i ); + /* DirectionType */ + ok( row->InterfaceAndOperStatusFlags.HardwareInterface == stat->flags.hw, "%d: mismatch\n", i ); + ok( row->InterfaceAndOperStatusFlags.FilterInterface == stat->flags.filter, "%d: mismatch\n", i ); + ok( row->InterfaceAndOperStatusFlags.ConnectorPresent == stat->conn_present, "%d: mismatch\n", i ); + /* NotAuthenticated */ + ok( row->InterfaceAndOperStatusFlags.NotMediaConnected == dyn->flags.not_media_conn, "%d: mismatch\n", i ); + /* Paused */ + /* LowPower */ + /* EndPointInterface */ + ok( row->OperStatus == dyn->oper_status, "%d: mismatch\n", i ); + ok( row->AdminStatus == rw->admin_status, "%d: mismatch\n", i ); + ok( row->MediaConnectState == dyn->media_conn_state, "%d: status mismatch\n", i ); + ok( IsEqualGUID( &row->NetworkGuid, &rw->network_guid ), "%d: mismatch\n", i ); + ok( row->ConnectionType == stat->conn_type, "%d: mismatch\n", i ); + if (dyn->xmit_speed == ~0ULL) dyn->xmit_speed = 0; + ok( row->TransmitLinkSpeed == dyn->xmit_speed, "%d: mismatch\n", i ); + if (dyn->rcv_speed == ~0ULL) dyn->rcv_speed = 0; + ok( row->ReceiveLinkSpeed == dyn->rcv_speed, "%d: mismatch\n", i ); + ok( bounded( row->InOctets, dyn->in_octets, dyn_2->in_octets ), "%d: mismatch\n", i ); + ok( bounded( row->InUcastPkts, dyn->in_ucast_pkts, dyn_2->in_ucast_pkts ), "%d: mismatch\n", i ); + ok( bounded( row->InNUcastPkts, dyn->in_mcast_pkts + dyn->in_bcast_pkts, + dyn_2->in_mcast_pkts + dyn_2->in_bcast_pkts ), "%d: mismatch\n", i ); + ok( bounded( row->InDiscards, dyn->in_discards, dyn_2->in_discards ), "%d: mismatch\n", i ); + ok( bounded( row->InErrors, dyn->in_errors, dyn_2->in_errors ), "%d: mismatch\n", i ); + /* InUnknownProtos */ + ok( bounded( row->InUcastOctets, dyn->in_ucast_octs, dyn_2->in_ucast_octs ), "%d: mismatch\n", i ); + ok( bounded( row->InMulticastOctets, dyn->in_mcast_octs, dyn_2->in_mcast_octs ), "%d: mismatch\n", i ); + ok( bounded( row->InBroadcastOctets, dyn->in_bcast_octs, dyn_2->in_bcast_octs ), "%d: mismatch\n", i ); + ok( bounded( row->OutOctets, dyn->out_octets, dyn_2->out_octets ), "%d: mismatch\n", i ); + ok( bounded( row->OutUcastPkts, dyn->out_ucast_pkts, dyn_2->out_ucast_pkts ), "%d: mismatch\n", i ); + ok( bounded( row->OutNUcastPkts, dyn->out_mcast_pkts + dyn->out_bcast_pkts, + dyn_2->out_mcast_pkts + dyn_2->out_bcast_pkts ), "%d: mismatch\n", i ); + ok( bounded( row->OutDiscards, dyn->out_discards, dyn_2->out_discards ), "%d: mismatch\n", i ); + ok( bounded( row->OutErrors, dyn->out_errors, dyn_2->out_errors ), "%d: mismatch\n", i ); + ok( bounded( row->OutUcastOctets, dyn->out_ucast_octs, dyn_2->out_ucast_octs ), "%d: mismatch\n", i ); + ok( bounded( row->OutMulticastOctets, dyn->out_mcast_octs, dyn_2->out_mcast_octs ), "%d: mismatch\n", i ); + ok( bounded( row->OutBroadcastOctets, dyn->out_bcast_octs, dyn_2->out_bcast_octs ), "%d: mismatch\n", i ); + /* OutQLen */ + } + + FreeMibTable( table ); + NsiFreeTable( luid_tbl_2, NULL, dyn_tbl_2, NULL ); + NsiFreeTable( luid_tbl, rw_tbl, dyn_tbl, stat_tbl ); +} + +START_TEST( nsi ) +{ + test_ndis_ifinfo(); +} diff --git a/include/wine/nsi.h b/include/wine/nsi.h index 594d6f9ea5d..362eaed0c4e 100644 --- a/include/wine/nsi.h +++ b/include/wine/nsi.h @@ -19,6 +19,79 @@ #ifndef __WINE_NSI_H #define __WINE_NSI_H
+/* Undocumented NSI NDIS tables */ +#define NSI_NDIS_IFINFO_TABLE 0 +#define NSI_NDIS_INDEX_LUID_TABLE 2 + +struct nsi_ndis_ifinfo_rw +{ + GUID network_guid; + DWORD admin_status; + IF_COUNTED_STRING alias; /* .Length in bytes not including '\0' */ + IF_PHYSICAL_ADDRESS phys_addr; + USHORT pad; + IF_COUNTED_STRING name2; + DWORD unk; +}; + +struct nsi_ndis_ifinfo_dynamic +{ + DWORD oper_status; + struct + { + DWORD unk : 1; + DWORD not_media_conn : 1; + DWORD unk2 : 30; + } flags; + DWORD media_conn_state; + DWORD unk; + DWORD mtu; + ULONG64 xmit_speed; + ULONG64 rcv_speed; + ULONG64 in_errors; + ULONG64 in_discards; + ULONG64 out_errors; + ULONG64 out_discards; + ULONG64 unk2; + ULONG64 in_octets; + ULONG64 in_ucast_pkts; + ULONG64 in_mcast_pkts; + ULONG64 in_bcast_pkts; + ULONG64 out_octets; + ULONG64 out_ucast_pkts; + ULONG64 out_mcast_pkts; + ULONG64 out_bcast_pkts; + ULONG64 unk3[2]; + ULONG64 in_ucast_octs; + ULONG64 in_mcast_octs; + ULONG64 in_bcast_octs; + ULONG64 out_ucast_octs; + ULONG64 out_mcast_octs; + ULONG64 out_bcast_octs; + ULONG64 unk4; +}; + +struct nsi_ndis_ifinfo_static +{ + DWORD if_index; + IF_COUNTED_STRING descr; + DWORD type; + DWORD access_type; + DWORD unk; + DWORD conn_type; + GUID if_guid; + USHORT conn_present; + IF_PHYSICAL_ADDRESS perm_phys_addr; + struct + { + DWORD hw : 1; + DWORD filter : 1; + DWORD unk : 30; + } flags; + DWORD media_type; + DWORD phys_medium_type; +}; + /* Undocumented Nsi api */ DWORD WINAPI NsiAllocateAndGetTable( DWORD unk, const NPI_MODULEID *module, DWORD table, void **key_data, DWORD key_size, void **rw_data, DWORD rw_size, void **dynamic_data, DWORD dynamic_size,
On 6/25/21 10:20 AM, Huw Davies wrote:
ok( row->InterfaceLuid.Value == luid->Value, "%d: mismatch\n", i );
ok( row->InterfaceIndex == stat->if_index, "%d: mismatch\n", i );
ok( IsEqualGUID( &row->InterfaceGuid, &stat->if_guid ), "%d: mismatch\n", i );
ok( !memcmp( row->Alias, rw->alias.String, rw->alias.Length ), "%d: mismatch\n", i );
ok( lstrlenW( row->Alias ) * 2 == rw->alias.Length, "%d: mismatch\n", i );
ok( !memcmp( row->Description, stat->descr.String, sizeof(row->Description) ), "%d: mismatch\n", i );
ok( lstrlenW( row->Description ) * 2 == stat->descr.Length, "%d: mismatch\n", i );
ok( row->PhysicalAddressLength == rw->phys_addr.Length, "%d: mismatch\n", i );
ok( !memcmp( row->PhysicalAddress, rw->phys_addr.Address, IF_MAX_PHYS_ADDRESS_LENGTH ), "%d: mismatch\n", i );
ok( row->PhysicalAddressLength == stat->perm_phys_addr.Length, "%d: mismatch\n", i );
ok( !memcmp( row->PermanentPhysicalAddress, stat->perm_phys_addr.Address, IF_MAX_PHYS_ADDRESS_LENGTH ), "%d: mismatch\n", i );
ok( row->Mtu == dyn->mtu, "%d: mismatch\n", i );
ok( row->Type == stat->type, "%d: mismatch\n", i );
/* TunnelType */
ok( row->MediaType == stat->media_type, "%d: mismatch\n", i );
ok( row->PhysicalMediumType == stat->phys_medium_type, "%d: mismatch\n", i );
ok( row->AccessType == stat->access_type, "%d: mismatch\n", i );
/* DirectionType */
ok( row->InterfaceAndOperStatusFlags.HardwareInterface == stat->flags.hw, "%d: mismatch\n", i );
ok( row->InterfaceAndOperStatusFlags.FilterInterface == stat->flags.filter, "%d: mismatch\n", i );
ok( row->InterfaceAndOperStatusFlags.ConnectorPresent == stat->conn_present, "%d: mismatch\n", i );
/* NotAuthenticated */
ok( row->InterfaceAndOperStatusFlags.NotMediaConnected == dyn->flags.not_media_conn, "%d: mismatch\n", i );
/* Paused */
/* LowPower */
/* EndPointInterface */
ok( row->OperStatus == dyn->oper_status, "%d: mismatch\n", i );
ok( row->AdminStatus == rw->admin_status, "%d: mismatch\n", i );
ok( row->MediaConnectState == dyn->media_conn_state, "%d: status mismatch\n", i );
ok( IsEqualGUID( &row->NetworkGuid, &rw->network_guid ), "%d: mismatch\n", i );
ok( row->ConnectionType == stat->conn_type, "%d: mismatch\n", i );
if (dyn->xmit_speed == ~0ULL) dyn->xmit_speed = 0;
ok( row->TransmitLinkSpeed == dyn->xmit_speed, "%d: mismatch\n", i );
if (dyn->rcv_speed == ~0ULL) dyn->rcv_speed = 0;
ok( row->ReceiveLinkSpeed == dyn->rcv_speed, "%d: mismatch\n", i );
ok( bounded( row->InOctets, dyn->in_octets, dyn_2->in_octets ), "%d: mismatch\n", i );
ok( bounded( row->InUcastPkts, dyn->in_ucast_pkts, dyn_2->in_ucast_pkts ), "%d: mismatch\n", i );
ok( bounded( row->InNUcastPkts, dyn->in_mcast_pkts + dyn->in_bcast_pkts,
dyn_2->in_mcast_pkts + dyn_2->in_bcast_pkts ), "%d: mismatch\n", i );
ok( bounded( row->InDiscards, dyn->in_discards, dyn_2->in_discards ), "%d: mismatch\n", i );
ok( bounded( row->InErrors, dyn->in_errors, dyn_2->in_errors ), "%d: mismatch\n", i );
/* InUnknownProtos */
ok( bounded( row->InUcastOctets, dyn->in_ucast_octs, dyn_2->in_ucast_octs ), "%d: mismatch\n", i );
ok( bounded( row->InMulticastOctets, dyn->in_mcast_octs, dyn_2->in_mcast_octs ), "%d: mismatch\n", i );
ok( bounded( row->InBroadcastOctets, dyn->in_bcast_octs, dyn_2->in_bcast_octs ), "%d: mismatch\n", i );
ok( bounded( row->OutOctets, dyn->out_octets, dyn_2->out_octets ), "%d: mismatch\n", i );
ok( bounded( row->OutUcastPkts, dyn->out_ucast_pkts, dyn_2->out_ucast_pkts ), "%d: mismatch\n", i );
ok( bounded( row->OutNUcastPkts, dyn->out_mcast_pkts + dyn->out_bcast_pkts,
dyn_2->out_mcast_pkts + dyn_2->out_bcast_pkts ), "%d: mismatch\n", i );
ok( bounded( row->OutDiscards, dyn->out_discards, dyn_2->out_discards ), "%d: mismatch\n", i );
ok( bounded( row->OutErrors, dyn->out_errors, dyn_2->out_errors ), "%d: mismatch\n", i );
ok( bounded( row->OutUcastOctets, dyn->out_ucast_octs, dyn_2->out_ucast_octs ), "%d: mismatch\n", i );
ok( bounded( row->OutMulticastOctets, dyn->out_mcast_octs, dyn_2->out_mcast_octs ), "%d: mismatch\n", i );
ok( bounded( row->OutBroadcastOctets, dyn->out_bcast_octs, dyn_2->out_bcast_octs ), "%d: mismatch\n", i );
/* OutQLen */
- }
FWIW we have a nice winetest_push_context / winetest_pop_context which saves the day in cases like this to prepend loop index on all messages.
Cheers,
On Fri, Jun 25, 2021 at 10:26:11AM +0200, Rémi Bernon wrote:
On 6/25/21 10:20 AM, Huw Davies wrote:
ok( bounded( row->OutUcastOctets, dyn->out_ucast_octs, dyn_2->out_ucast_octs ), "%d: mismatch\n", i );
ok( bounded( row->OutMulticastOctets, dyn->out_mcast_octs, dyn_2->out_mcast_octs ), "%d: mismatch\n", i );
ok( bounded( row->OutBroadcastOctets, dyn->out_bcast_octs, dyn_2->out_bcast_octs ), "%d: mismatch\n", i );
/* OutQLen */
- }
FWIW we have a nice winetest_push_context / winetest_pop_context which saves the day in cases like this to prepend loop index on all messages.
Yes we do, good point! I'll send in a new version using that.
Huw.