-- v2: iphlpapi: Implement GetIpInterfaceEntry(). nsi/tests: Add tests for IP interface table. iphlpapi: Implement GetIpInterfaceTable(). nsiproxy.sys: Implement IP interface table.
From: Paul Gofman pgofman@codeweavers.com
--- dlls/nsiproxy.sys/ip.c | 195 +++++++++++++++++++++++++++++++ dlls/nsiproxy.sys/unix_private.h | 23 ++++ include/wine/nsi.h | 42 +++++++ 3 files changed, 260 insertions(+)
diff --git a/dlls/nsiproxy.sys/ip.c b/dlls/nsiproxy.sys/ip.c index 1576ce1c2a1..65784cb5aa1 100644 --- a/dlls/nsiproxy.sys/ip.c +++ b/dlls/nsiproxy.sys/ip.c @@ -810,6 +810,183 @@ static NTSTATUS ipv6_ipstats_get_all_parameters( const void *key, UINT key_size, #endif }
+static NTSTATUS ip_interface_fill( UINT fam, const char *unix_name, void *key_data, UINT key_size, + void *rw_data, UINT rw_size, void *dynamic_data, UINT dynamic_size, + void *static_data, UINT static_size, UINT_PTR *count ) +{ + BOOL want_data = key_size || rw_size || dynamic_size || static_size; + int base_reachable_time, dad_transmits, site_prefix_len; + struct nsi_ip_interface_dynamic *dyn = dynamic_data; + struct nsi_ip_interface_static *stat = static_data; + struct nsi_ndis_ifinfo_dynamic iface_dynamic; + struct nsi_ip_interface_key *key = key_data; + struct nsi_ndis_ifinfo_static iface_static; + struct ipv6_addr_scope *addr_scopes = NULL; + unsigned int addr_scopes_size = 0; + struct nsi_ip_interface_rw *rw = rw_data; + struct ifaddrs *addrs, *entry, *entry2; + UINT num = 0, scope_id = 0xffffffff; + char path[256]; + NET_LUID luid; + + if (getifaddrs( &addrs )) return STATUS_NO_MORE_ENTRIES; + + if (fam == AF_INET6) addr_scopes = get_ipv6_addr_scope_table( &addr_scopes_size ); + + rw = rw_data; + for (entry = addrs; entry; entry = entry->ifa_next) + { + if (!entry->ifa_addr || entry->ifa_addr->sa_family != fam) continue; + if (unix_name && strcmp( entry->ifa_name, unix_name )) continue; + if (fam == AF_INET6) + { + scope_id = find_ipv6_addr_scope( (IN6_ADDR*)&((struct sockaddr_in6 *)entry->ifa_addr)->sin6_addr, addr_scopes, + addr_scopes_size ); + /* Info in the IP interface table entry corresponds to link local IPv6 address, while reported info for + * loopback is different on Windows. */ + if (scope_id != 0xffffffff && scope_id != 0x1000 /* loopback */ && scope_id != 0x2000 /* link_local */) + continue; + } + if (!unix_name) + { + /* getifaddrs may return multipe entries for the same interface having different IP addresses. + * IP interface table being returned has only one entry per network interface. */ + for (entry2 = addrs; entry2 && entry2 != entry; entry2 = entry2->ifa_next) + { + if (!entry2->ifa_addr || entry2->ifa_addr->sa_family != fam) continue; + if (fam == AF_INET6) + { + scope_id = find_ipv6_addr_scope( (IN6_ADDR*)&((struct sockaddr_in6 *)entry2->ifa_addr)->sin6_addr, + addr_scopes, addr_scopes_size ); + if (scope_id != 0xffffffff && scope_id != 0x1000 /* loopback */ + && scope_id != 0x2000 /* link_local */) continue; + } + if (!strcmp( entry2->ifa_name, entry->ifa_name )) + break; + } + if (entry2 != entry) continue; + } + if (!convert_unix_name_to_luid( entry->ifa_name, &luid )) continue; + if (!nsi_get_all_parameters( &NPI_MS_NDIS_MODULEID, NSI_NDIS_IFINFO_TABLE, &luid, sizeof(luid), + NULL, 0, &iface_dynamic, sizeof(iface_dynamic), + &iface_static, sizeof(iface_static) )) + { + ERR( "Could not get iface parameters.\n" ); + continue; + } + + if (!count || num < *count) + { + if (key && count) memcpy( key, &luid, sizeof(luid) ); + if (stat) memset( stat, 0, sizeof(*stat) ); + + base_reachable_time = 0; + if (iface_static.type == MIB_IF_TYPE_LOOPBACK) dad_transmits = 0; + else dad_transmits = (fam == AF_INET6) ? 1 : 3; +#if __linux__ + if (rw || dyn) + { + sprintf( path, "/proc/sys/net/%s/neigh/%s/base_reachable_time_ms", + (fam == AF_INET) ? "ipv4" : "ipv6", entry->ifa_name); + read_sysctl_int( path, &base_reachable_time ); + } + if (rw) + { + if (fam == AF_INET6 && iface_static.type != MIB_IF_TYPE_LOOPBACK) + { + sprintf( path, "/proc/sys/net/ipv6/conf/%s/dad_transmits", entry->ifa_name); + read_sysctl_int( path, &dad_transmits ); + } + } +#endif + if (rw) + { + site_prefix_len = 64; + if (fam == AF_INET6 && iface_static.type != MIB_IF_TYPE_LOOPBACK) + { + /* For some reason prefix length reported on ipv4 is 64 for ipv4 addresses on Windows and + * prefix len is 64 for loopback device. */ + site_prefix_len = mask_v6_to_prefix( &((struct sockaddr_in6 *)entry->ifa_netmask)->sin6_addr ); + } + memset( rw, 0, sizeof(*rw) ); + rw->mtu = iface_dynamic.mtu; + rw->site_prefix_len = site_prefix_len; + rw->base_reachable_time = base_reachable_time; + rw->dad_transmits = dad_transmits; + rw->retransmit_time = 1000; + rw->path_mtu_discovery_timeout = 600000; + rw->link_local_address_behavior = (fam == AF_INET6) ? LinkLocalAlwaysOn : LinkLocalDelayed; + rw->link_local_address_timeout = (fam == AF_INET6) ? 0 : 6500; + } + if (dyn) + { + memset( dyn, 0, sizeof(*dyn) ); + dyn->if_index = iface_static.if_index; + dyn->connected = (iface_dynamic.media_conn_state == MediaConnectStateConnected); + dyn->reachable_time = base_reachable_time; + } + } + if (!count) + { + freeifaddrs( addrs ); + free( addr_scopes ); + return STATUS_SUCCESS; + } + ++num; + if (key) ++key; + if (rw) ++rw; + if (dyn) ++dyn; + if (stat) ++stat; + } + freeifaddrs( addrs ); + free( addr_scopes ); + + if (!count) return STATUS_NOT_FOUND; + if (want_data && num > *count) return STATUS_BUFFER_OVERFLOW; + *count = num; + return STATUS_SUCCESS; +} + +static NTSTATUS ipv4_interface_enumerate_all( void *key_data, UINT key_size, void *rw_data, UINT rw_size, + void *dynamic_data, UINT dynamic_size, + void *static_data, UINT static_size, UINT_PTR *count ) +{ + return ip_interface_fill( AF_INET, NULL, key_data, key_size, rw_data, rw_size, dynamic_data, dynamic_size, + static_data, static_size, count ); +} + +static NTSTATUS ipv4_interface_get_all_parameters( const void *key, UINT key_size, void *rw_data, UINT rw_size, + void *dynamic_data, UINT dynamic_size, void *static_data, UINT static_size ) +{ + struct nsi_ip_interface_key *ip_key = (void *)key; + const char *unix_name; + + if (!convert_luid_to_unix_name( &ip_key->luid, &unix_name )) return STATUS_NOT_FOUND; + + return ip_interface_fill( AF_INET, unix_name, ip_key, key_size, rw_data, rw_size, dynamic_data, dynamic_size, + static_data, static_size, NULL ); +} + +static NTSTATUS ipv6_interface_enumerate_all( void *key_data, UINT key_size, void *rw_data, UINT rw_size, + void *dynamic_data, UINT dynamic_size, + void *static_data, UINT static_size, UINT_PTR *count ) +{ + return ip_interface_fill( AF_INET6, NULL, key_data, key_size, rw_data, rw_size, dynamic_data, dynamic_size, + static_data, static_size, count ); +} + +static NTSTATUS ipv6_interface_get_all_parameters( const void *key, UINT key_size, void *rw_data, UINT rw_size, + void *dynamic_data, UINT dynamic_size, void *static_data, UINT static_size ) +{ + struct nsi_ip_interface_key *ip_key = (void *)key; + const char *unix_name; + + if (!convert_luid_to_unix_name( &ip_key->luid, &unix_name )) return STATUS_NOT_FOUND; + + return ip_interface_fill( AF_INET6, unix_name, ip_key, key_size, rw_data, rw_size, dynamic_data, dynamic_size, + static_data, static_size, NULL ); +} + static void unicast_fill_entry( struct ifaddrs *entry, void *key, struct nsi_ip_unicast_rw *rw, struct nsi_ip_unicast_dynamic *dyn, struct nsi_ip_unicast_static *stat ) { @@ -1660,6 +1837,15 @@ static struct module_table ipv4_tables[] = NULL, ipv4_ipstats_get_all_parameters, }, + { + NSI_IP_INTERFACE_TABLE, + { + sizeof(struct nsi_ip_interface_key), sizeof(struct nsi_ip_interface_rw), + sizeof(struct nsi_ip_interface_dynamic), sizeof(struct nsi_ip_interface_static) + }, + ipv4_interface_enumerate_all, + ipv4_interface_get_all_parameters, + }, { NSI_IP_UNICAST_TABLE, { @@ -1725,6 +1911,15 @@ static struct module_table ipv6_tables[] = NULL, ipv6_ipstats_get_all_parameters, }, + { + NSI_IP_INTERFACE_TABLE, + { + sizeof(struct nsi_ip_interface_key), sizeof(struct nsi_ip_interface_rw), + sizeof(struct nsi_ip_interface_dynamic), sizeof(struct nsi_ip_interface_static) + }, + ipv6_interface_enumerate_all, + ipv6_interface_get_all_parameters, + }, { NSI_IP_UNICAST_TABLE, { diff --git a/dlls/nsiproxy.sys/unix_private.h b/dlls/nsiproxy.sys/unix_private.h index b7c56710e22..5487de5111d 100644 --- a/dlls/nsiproxy.sys/unix_private.h +++ b/dlls/nsiproxy.sys/unix_private.h @@ -98,6 +98,29 @@ static inline BOOL convert_index_to_luid( UINT index, NET_LUID *luid ) return !nsi_get_parameter_ex( ¶ms ); }
+static inline BOOL nsi_get_all_parameters( const NPI_MODULEID *module, UINT table, + void *key_data, UINT key_size, void *rw_data, UINT rw_size, + void *dynamic_data, UINT dynamic_size, void *static_data, UINT static_size ) +{ + struct nsi_get_all_parameters_ex params; + + params.unknown[0] = 0; + params.unknown[1] = 0; + params.module = module; + params.table = table; + params.first_arg = 1; + params.unknown2 = 0; + params.key = key_data; + params.key_size = key_size; + params.rw_data = rw_data; + params.rw_size = rw_size; + params.dynamic_data = dynamic_data; + params.dynamic_size = dynamic_size; + params.static_data = static_data; + params.static_size = static_size; + return !nsi_get_all_parameters_ex( ¶ms ); +} + struct ipv6_addr_scope { IN6_ADDR addr; diff --git a/include/wine/nsi.h b/include/wine/nsi.h index 6bf3ce5bd5d..0df313db624 100644 --- a/include/wine/nsi.h +++ b/include/wine/nsi.h @@ -24,6 +24,7 @@ #include "ws2def.h" #include "ws2ipdef.h" #include "winioctl.h" +#include "nldef.h"
/* Undocumented NSI NDIS tables */ #define NSI_NDIS_IFINFO_TABLE 0 @@ -101,6 +102,7 @@ struct nsi_ndis_ifinfo_static #define NSI_IP_COMPARTMENT_TABLE 2 #define NSI_IP_ICMPSTATS_TABLE 3 #define NSI_IP_IPSTATS_TABLE 6 +#define NSI_IP_INTERFACE_TABLE 7 #define NSI_IP_UNICAST_TABLE 10 #define NSI_IP_NEIGHBOUR_TABLE 11 #define NSI_IP_FORWARD_TABLE 16 @@ -293,6 +295,46 @@ struct nsi_ip_forward_static UINT if_index; };
+struct nsi_ip_interface_key +{ + NET_LUID luid; +}; + +struct nsi_ip_interface_rw +{ + UINT32 unk1[5]; + UINT router_discovery_behaviour; + UINT32 unk2; + ULONG metric; + ULONG base_reachable_time; + ULONG retransmit_time; + ULONG path_mtu_discovery_timeout; + ULONG dad_transmits; + UINT link_local_address_behavior; + ULONG link_local_address_timeout; + ULONG zone_indices[ScopeLevelCount]; + ULONG mtu; + ULONG site_prefix_len; + UINT32 unk3[28]; +}; + +struct nsi_ip_interface_dynamic +{ + UINT if_index; + UINT unk1; + UINT supports_wakeup_patterns; + ULONG reachable_time; + UINT connected; + UINT unk2[3]; + NL_INTERFACE_OFFLOAD_ROD transmit_offload; + UINT32 unk3[13]; +}; + +struct nsi_ip_interface_static +{ + UINT32 unk[8]; +}; + /* Undocumented NSI TCP tables */ #define NSI_TCP_STATS_TABLE 0 #define NSI_TCP_ALL_TABLE 3
From: Paul Gofman pgofman@codeweavers.com
--- dlls/iphlpapi/iphlpapi_main.c | 92 ++++++++++++++++++++++++++++++++-- dlls/iphlpapi/tests/iphlpapi.c | 59 ++++++++++++++++++++++ 2 files changed, 148 insertions(+), 3 deletions(-)
diff --git a/dlls/iphlpapi/iphlpapi_main.c b/dlls/iphlpapi/iphlpapi_main.c index 78000a628b7..4c34318b2ce 100644 --- a/dlls/iphlpapi/iphlpapi_main.c +++ b/dlls/iphlpapi/iphlpapi_main.c @@ -4530,13 +4530,99 @@ char *WINAPI IPHLP_if_indextoname( NET_IFINDEX index, char *name ) return name; }
+static void fill_ip_interface_table_row( ADDRESS_FAMILY fam, MIB_IPINTERFACE_ROW *row, struct nsi_ip_interface_key *key, + struct nsi_ip_interface_rw *rw, struct nsi_ip_interface_dynamic *dyn ) +{ + row->Family = fam; + row->InterfaceLuid = key->luid; + row->InterfaceIndex = dyn->if_index; + row->RouterDiscoveryBehavior = rw->router_discovery_behaviour; + row->DadTransmits = rw->dad_transmits; + row->BaseReachableTime = rw->base_reachable_time; + row->RetransmitTime = rw->retransmit_time; + row->PathMtuDiscoveryTimeout = rw->path_mtu_discovery_timeout; + row->LinkLocalAddressBehavior = rw->link_local_address_behavior; + row->LinkLocalAddressTimeout = rw->link_local_address_timeout; + memcpy( row->ZoneIndices, rw->zone_indices, sizeof(row->ZoneIndices) ); + row->SitePrefixLength = rw->site_prefix_len; + row->Metric = rw->metric; + row->NlMtu = rw->mtu; + row->Connected = dyn->connected; + row->SupportsWakeUpPatterns = dyn->supports_wakeup_patterns; + row->ReachableTime = dyn->reachable_time; + row->TransmitOffload = dyn->transmit_offload; + + /* MinRouterAdvertisementInterval / MaxRouterAdvertisementInterval don't seem to have an + * entry in NSI interface table, or maybe these fields are usually default and NSI tables have 0 in that case + * and thus weren't discovered. */ + row->MinRouterAdvertisementInterval = 200; + row->MaxRouterAdvertisementInterval = 600; + + /* Flags exact locations were not yet discovered in NSI table. */ + row->UseAutomaticMetric = 1; + row->UseNeighborUnreachabilityDetection = 1; + row->SupportsNeighborDiscovery = 1; + row->SupportsRouterDiscovery = 1; +} + /****************************************************************** * GetIpInterfaceTable (IPHLPAPI.@) */ -DWORD WINAPI GetIpInterfaceTable(ADDRESS_FAMILY family, PMIB_IPINTERFACE_TABLE *table) +DWORD WINAPI GetIpInterfaceTable( ADDRESS_FAMILY family, MIB_IPINTERFACE_TABLE **table ) { - FIXME("(%u %p): stub\n", family, table); - return ERROR_NOT_SUPPORTED; + struct nsi_ip_interface_dynamic *dyn; + struct nsi_ip_interface_key *keys; + DWORD err, count, total_count = 0; + MIB_IPINTERFACE_TABLE *new_alloc; + struct nsi_ip_interface_rw *rw; + ADDRESS_FAMILY fam[3] = { 0 }; + unsigned int i, family_idx; + + TRACE( "(%u %p).\n", family, table ); + + if (!table) return ERROR_INVALID_PARAMETER; + + if (family == AF_UNSPEC) + { + fam[0] = AF_INET; + fam[1] = AF_INET6; + } + else fam[0] = family; + + *table = NULL; + for (family_idx = 0; fam[family_idx]; ++family_idx) + { + err = NsiAllocateAndGetTable( 1, ip_module_id( fam[family_idx] ), NSI_IP_INTERFACE_TABLE, + (void **)&keys, sizeof(*keys), (void **)&rw, sizeof(*rw), + (void **)&dyn, sizeof(*dyn), NULL, 0, &count, 0 ); + if (err) + { + heap_free( *table ); + return err; + } + total_count += count; + new_alloc = heap_alloc_zero( offsetof(MIB_IPINTERFACE_TABLE, Table[total_count]) ); + if (!new_alloc) + { + heap_free( *table ); + NsiFreeTable( keys, rw, dyn, NULL ); + return ERROR_NOT_ENOUGH_MEMORY; + } + if (*table) + { + memcpy( new_alloc, *table, offsetof(MIB_IPINTERFACE_TABLE, Table[(*table)->NumEntries]) ); + free( *table ); + } + *table = new_alloc; + for (i = 0; i < count; ++i) + { + fill_ip_interface_table_row( fam[family_idx], &(*table)->Table[(*table)->NumEntries], + &keys[i], &rw[i], &dyn[i] ); + ++(*table)->NumEntries; + } + NsiFreeTable( keys, rw, dyn, NULL ); + } + return ERROR_SUCCESS; }
/****************************************************************** diff --git a/dlls/iphlpapi/tests/iphlpapi.c b/dlls/iphlpapi/tests/iphlpapi.c index 397b2e8fad9..dfe067798a6 100644 --- a/dlls/iphlpapi/tests/iphlpapi.c +++ b/dlls/iphlpapi/tests/iphlpapi.c @@ -67,6 +67,7 @@ static DWORD (WINAPI *pParseNetworkString)(const WCHAR*,DWORD,NET_ADDRESS_INFO*, static DWORD (WINAPI *pNotifyUnicastIpAddressChange)(ADDRESS_FAMILY, PUNICAST_IPADDRESS_CHANGE_CALLBACK, PVOID, BOOLEAN, HANDLE *); static DWORD (WINAPI *pCancelMibChangeNotify2)(HANDLE); +static DWORD (WINAPI *pGetIpInterfaceTable)(ADDRESS_FAMILY family, MIB_IPINTERFACE_TABLE **table);
DWORD WINAPI ConvertGuidToStringA( const GUID *, char *, DWORD ); DWORD WINAPI ConvertGuidToStringW( const GUID *, WCHAR *, DWORD ); @@ -88,6 +89,7 @@ static void loadIPHlpApi(void) pParseNetworkString = (void *)GetProcAddress(hLibrary, "ParseNetworkString"); pNotifyUnicastIpAddressChange = (void *)GetProcAddress(hLibrary, "NotifyUnicastIpAddressChange"); pCancelMibChangeNotify2 = (void *)GetProcAddress(hLibrary, "CancelMibChangeNotify2"); + pGetIpInterfaceTable = (void *)GetProcAddress(hLibrary, "GetIpInterfaceTable"); } }
@@ -3106,6 +3108,62 @@ static void test_compartments(void) ok(id == NET_IF_COMPARTMENT_ID_PRIMARY, "got %u\n", id); }
+static void test_GetIpInterfaceTable(void) +{ + MIB_IPINTERFACE_TABLE *table; + MIB_IF_ROW2 *if_info = NULL; + MIB_IPINTERFACE_ROW *row; + MIB_IF_TABLE2 *if_table; + unsigned int i, j; + BOOL connected, is_loopback, loopback_found = FALSE; + DWORD err; + + if (!pGetIpInterfaceTable) + { + win_skip( "GetIpInterfaceTable is not available\n" ); + return; + } + + err = GetIfTable2( &if_table ); + ok( !err, "got %ld\n", err ); + + err = pGetIpInterfaceTable( AF_UNSPEC, NULL ); + ok( err == ERROR_INVALID_PARAMETER, "got %lu.\n", err ); + + err = pGetIpInterfaceTable( AF_UNSPEC, &table ); + ok( !err, "got %lu.\n", err ); + for (i = 0; i < table->NumEntries; ++i) + { + row = &table->Table[i]; + ok( row->Family == AF_INET || row->Family == AF_INET6, "got %d.\n", row->Family ); + for (j = 0; j < if_table->NumEntries; ++j) + { + if_info = &if_table->Table[j]; + if (if_info->InterfaceIndex == row->InterfaceIndex) break; + } + ok( j < if_table->NumEntries, "could not find interface.\n" ); + ok( row->InterfaceLuid.Value == if_info->InterfaceLuid.Value, "luid doesn't match.\n" ); + is_loopback = (if_info->Type == IF_TYPE_SOFTWARE_LOOPBACK); + if (is_loopback) loopback_found = TRUE; + if (row->Family == AF_INET) + ok( row->SitePrefixLength == 64, "got %lu.\n", row->SitePrefixLength ); + if (is_loopback) + { + ok( !row->DadTransmits, "got %lu.\n", row->DadTransmits ); + ok( row->SitePrefixLength == 64, "got %lu.\n", row->SitePrefixLength ); + } + ok( row->MinRouterAdvertisementInterval == 200, "got %lu.\n", row->MinRouterAdvertisementInterval ); + ok( row->MaxRouterAdvertisementInterval == 600, "got %lu.\n", row->MaxRouterAdvertisementInterval ); + if (is_loopback) + todo_wine ok( row->NlMtu == ~0u, "got %lu.\n", row->NlMtu ); + connected = (if_info->MediaConnectState == MediaConnectStateConnected); + ok( row->Connected == connected, "got %d, expected %d.\n", row->Connected, connected ); + } + ok( loopback_found, "loopback not found.\n" ); + FreeMibTable( table ); + FreeMibTable( if_table ); +} + START_TEST(iphlpapi) { WSADATA wsa_data; @@ -3147,6 +3205,7 @@ START_TEST(iphlpapi) test_NotifyUnicastIpAddressChange(); test_ConvertGuidToString(); test_compartments(); + test_GetIpInterfaceTable(); freeIPHlpApi(); }
From: Paul Gofman pgofman@codeweavers.com
--- dlls/nsi/tests/nsi.c | 64 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+)
diff --git a/dlls/nsi/tests/nsi.c b/dlls/nsi/tests/nsi.c index 0697e09b1e0..6e10b4edad7 100644 --- a/dlls/nsi/tests/nsi.c +++ b/dlls/nsi/tests/nsi.c @@ -802,6 +802,68 @@ static void test_ip_forward( int family ) winetest_pop_context(); }
+static void test_ip_interface( int family ) +{ + const NPI_MODULEID *mod = (family == AF_INET) ? &NPI_MS_IPV4_MODULEID : &NPI_MS_IPV6_MODULEID; + struct nsi_ip_interface_key *key_tbl; + struct nsi_ip_interface_rw *rw_tbl, *rw; + struct nsi_ip_interface_dynamic *dyn_tbl, *dyn; + struct nsi_ip_interface_static *stat_tbl; + MIB_IPINTERFACE_TABLE *table; + MIB_IPINTERFACE_ROW *row; + unsigned int i; + DWORD err, count; + + winetest_push_context( family == AF_INET ? "AF_INET" : "AF_INET6" ); + + err = GetIpInterfaceTable( family, &table ); + ok( !err, "got %lu.\n", err ); + err = NsiAllocateAndGetTable( 1, mod, NSI_IP_INTERFACE_TABLE, (void **)&key_tbl, sizeof(*key_tbl), + (void **)&rw_tbl, sizeof(*rw_tbl), (void **)&dyn_tbl, sizeof(*dyn_tbl), + (void **)&stat_tbl, sizeof(*stat_tbl), &count, 0 ); + ok( !err, "got %lu.\n", err ); + for (i = 0; i < count; ++i) + { + row = &table->Table[i]; + rw = &rw_tbl[i]; + dyn = &dyn_tbl[i]; + if (row->BaseReachableTime != rw->base_reachable_time) + { + /* Before Win10 1709. */ + win_skip( "Old NSI tables layout, skipping tests.\n" ); + break; + } + ok( row->BaseReachableTime == rw->base_reachable_time, "mismatch: %#lx vs %#lx.\n", + row->BaseReachableTime, rw->base_reachable_time ); + ok( row->Metric == rw->metric, "mismatch: %#lx vs %#lx.\n", row->Metric, rw->metric ); + ok( row->RetransmitTime == rw->retransmit_time, "mismatch: %#lx vs %#lx.\n", row->RetransmitTime, rw->retransmit_time ); + ok( row->PathMtuDiscoveryTimeout == rw->path_mtu_discovery_timeout, "mismatch: %#lx vs %#lx.\n", + row->PathMtuDiscoveryTimeout, rw->path_mtu_discovery_timeout ); + ok( row->DadTransmits == rw->dad_transmits, "mismatch: %#lx vs %#lx.\n", row->DadTransmits, rw->dad_transmits ); + ok( row->LinkLocalAddressBehavior == rw->link_local_address_behavior, "mismatch: %#x vs %#x.\n", + row->LinkLocalAddressBehavior, rw->link_local_address_behavior ); + ok( row->LinkLocalAddressTimeout == rw->link_local_address_timeout, "mismatch: %#lx vs %#lx.\n", + row->LinkLocalAddressTimeout, rw->link_local_address_timeout ); + ok( !memcmp( row->ZoneIndices, rw->zone_indices, sizeof(row->ZoneIndices) ), "mismatch.\n" ); + ok( row->NlMtu == rw->mtu, "mismatch: %#lx vs %#lx.\n", row->NlMtu, rw->mtu ); + ok( row->SitePrefixLength == rw->site_prefix_len, "mismatch: %#lx vs %#lx.\n", + row->SitePrefixLength, rw->site_prefix_len ); + ok( row->RouterDiscoveryBehavior == rw->router_discovery_behaviour, "mismatch: %#x vs %#x.\n", + row->RouterDiscoveryBehavior, rw->router_discovery_behaviour ); + + ok( row->InterfaceIndex == dyn->if_index, "mismatch: %#lx vs %#x.\n", row->InterfaceIndex, dyn->if_index ); + ok( row->SupportsWakeUpPatterns == dyn->supports_wakeup_patterns, "mismatch: %#x vs %#x.\n", + row->SupportsWakeUpPatterns, dyn->supports_wakeup_patterns ); + ok( row->ReachableTime == dyn->reachable_time, "mismatch: %#lx vs %#lx.\n", row->InterfaceIndex, dyn->reachable_time ); + ok( row->Connected == dyn->connected, "mismatch: %#x vs %#x.\n", row->Connected, dyn->connected ); + ok( *(UINT *)&row->TransmitOffload == *(UINT *)&dyn->transmit_offload, "mismatch: %#x vs %#x.\n", + *(UINT *)&row->TransmitOffload, *(UINT *)&dyn->transmit_offload ); + } + FreeMibTable( table ); + NsiFreeTable( key_tbl, rw_tbl, dyn_tbl, stat_tbl ); + winetest_pop_context(); +} + static void test_tcp_stats( int family ) { DWORD err; @@ -1111,6 +1173,8 @@ START_TEST( nsi ) test_ip_neighbour( AF_INET6 ); test_ip_forward( AF_INET ); test_ip_forward( AF_INET6 ); + test_ip_interface( AF_INET ); + test_ip_interface( AF_INET6 );
test_tcp_stats( AF_INET ); test_tcp_stats( AF_INET6 );
From: Paul Gofman pgofman@codeweavers.com
--- dlls/iphlpapi/iphlpapi.spec | 2 +- dlls/iphlpapi/iphlpapi_main.c | 27 +++++++++++++++++ dlls/iphlpapi/tests/iphlpapi.c | 53 +++++++++++++++++++++++++++++++--- include/netioapi.h | 1 + 4 files changed, 78 insertions(+), 5 deletions(-)
diff --git a/dlls/iphlpapi/iphlpapi.spec b/dlls/iphlpapi/iphlpapi.spec index facb52e0448..c04560ad656 100644 --- a/dlls/iphlpapi/iphlpapi.spec +++ b/dlls/iphlpapi/iphlpapi.spec @@ -106,7 +106,7 @@ @ stdcall GetIpForwardTable( ptr ptr long ) @ stdcall GetIpForwardTable2( long ptr ) @ stub GetIpForwardTableFromStack -#@ stub GetIpInterfaceEntry +@ stdcall GetIpInterfaceEntry( ptr ) @ stdcall GetIpInterfaceTable( long ptr ) #@ stub GetIpNetEntry2 @ stdcall GetIpNetTable( ptr ptr long ) diff --git a/dlls/iphlpapi/iphlpapi_main.c b/dlls/iphlpapi/iphlpapi_main.c index 4c34318b2ce..94769ed09fe 100644 --- a/dlls/iphlpapi/iphlpapi_main.c +++ b/dlls/iphlpapi/iphlpapi_main.c @@ -4625,6 +4625,33 @@ DWORD WINAPI GetIpInterfaceTable( ADDRESS_FAMILY family, MIB_IPINTERFACE_TABLE * return ERROR_SUCCESS; }
+/****************************************************************** + * GetIpInterfaceEntry (IPHLPAPI.@) + */ +DWORD WINAPI GetIpInterfaceEntry( MIB_IPINTERFACE_ROW *row ) +{ + struct nsi_ip_interface_dynamic dyn; + struct nsi_ip_interface_key key; + struct nsi_ip_interface_rw rw; + DWORD err; + + TRACE( "%p.\n", row ); + + if (!row) return ERROR_INVALID_PARAMETER; + if (row->Family != AF_INET && row->Family != AF_INET6) return ERROR_INVALID_PARAMETER; + + key.luid = row->InterfaceLuid; + if (!key.luid.Value && ConvertInterfaceIndexToLuid( row->InterfaceIndex, &key.luid )) return ERROR_NOT_FOUND; + + err = NsiGetAllParameters( 1, ip_module_id( row->Family ), NSI_IP_INTERFACE_TABLE, + &key, sizeof(key), &rw, sizeof(rw), + &dyn, sizeof(dyn), NULL, 0 ); + if (err) return err; + fill_ip_interface_table_row( row->Family, row, &key, &rw, &dyn ); + return ERROR_SUCCESS; +} + + /****************************************************************** * GetBestRoute2 (IPHLPAPI.@) */ diff --git a/dlls/iphlpapi/tests/iphlpapi.c b/dlls/iphlpapi/tests/iphlpapi.c index dfe067798a6..e189fd795f8 100644 --- a/dlls/iphlpapi/tests/iphlpapi.c +++ b/dlls/iphlpapi/tests/iphlpapi.c @@ -67,6 +67,7 @@ static DWORD (WINAPI *pParseNetworkString)(const WCHAR*,DWORD,NET_ADDRESS_INFO*, static DWORD (WINAPI *pNotifyUnicastIpAddressChange)(ADDRESS_FAMILY, PUNICAST_IPADDRESS_CHANGE_CALLBACK, PVOID, BOOLEAN, HANDLE *); static DWORD (WINAPI *pCancelMibChangeNotify2)(HANDLE); +static DWORD (WINAPI *pGetIpInterfaceEntry)(MIB_IPINTERFACE_ROW*); static DWORD (WINAPI *pGetIpInterfaceTable)(ADDRESS_FAMILY family, MIB_IPINTERFACE_TABLE **table);
DWORD WINAPI ConvertGuidToStringA( const GUID *, char *, DWORD ); @@ -90,6 +91,7 @@ static void loadIPHlpApi(void) pNotifyUnicastIpAddressChange = (void *)GetProcAddress(hLibrary, "NotifyUnicastIpAddressChange"); pCancelMibChangeNotify2 = (void *)GetProcAddress(hLibrary, "CancelMibChangeNotify2"); pGetIpInterfaceTable = (void *)GetProcAddress(hLibrary, "GetIpInterfaceTable"); + pGetIpInterfaceEntry = (void *)GetProcAddress(hLibrary, "GetIpInterfaceEntry"); } }
@@ -3108,8 +3110,9 @@ static void test_compartments(void) ok(id == NET_IF_COMPARTMENT_ID_PRIMARY, "got %u\n", id); }
-static void test_GetIpInterfaceTable(void) +static void test_GetIpInterface(void) { + MIB_IPINTERFACE_ROW entry_row; MIB_IPINTERFACE_TABLE *table; MIB_IF_ROW2 *if_info = NULL; MIB_IPINTERFACE_ROW *row; @@ -3118,9 +3121,9 @@ static void test_GetIpInterfaceTable(void) BOOL connected, is_loopback, loopback_found = FALSE; DWORD err;
- if (!pGetIpInterfaceTable) + if (!pGetIpInterfaceTable || !pGetIpInterfaceEntry) { - win_skip( "GetIpInterfaceTable is not available\n" ); + win_skip( "GetIpInterfaceTable or GetIpInterfaceEntry is not available\n" ); return; }
@@ -3158,6 +3161,48 @@ static void test_GetIpInterfaceTable(void) todo_wine ok( row->NlMtu == ~0u, "got %lu.\n", row->NlMtu ); connected = (if_info->MediaConnectState == MediaConnectStateConnected); ok( row->Connected == connected, "got %d, expected %d.\n", row->Connected, connected ); + + err = GetIpInterfaceEntry( NULL ); + ok( err == ERROR_INVALID_PARAMETER, "got %ld\n", err ); + + memset( &entry_row, 0, sizeof(entry_row) ); + entry_row.Family = AF_UNSPEC; + entry_row.InterfaceLuid = row->InterfaceLuid; + err = GetIpInterfaceEntry( &entry_row ); + ok( err == ERROR_INVALID_PARAMETER, "got %ld\n", err ); + + memset( &entry_row, 0xcc, sizeof(entry_row) ); + entry_row.Family = row->Family; + entry_row.InterfaceLuid = row->InterfaceLuid; + err = pGetIpInterfaceEntry( &entry_row ); + ok( !err, "got %ld\n", err ); + ok( entry_row.Family == row->Family, "got %d, expected %d.\n", entry_row.Family, row->Family ); + ok( entry_row.InterfaceLuid.Value == row->InterfaceLuid.Value, "got %#I64x, expected %#I64x.\n", + entry_row.InterfaceLuid.Value, row->InterfaceLuid.Value ); + ok( entry_row.InterfaceIndex == row->InterfaceIndex, "got %lu, expected %lu.\n", + entry_row.InterfaceIndex, row->InterfaceIndex ); + ok( entry_row.BaseReachableTime == row->BaseReachableTime, "got %lu, expected %lu.\n", + entry_row.BaseReachableTime, row->BaseReachableTime ); + + memset( &entry_row, 0xcc, sizeof(entry_row) ); + entry_row.Family = row->Family; + entry_row.InterfaceIndex = row->InterfaceIndex; + err = GetIpInterfaceEntry( &entry_row ); + ok( err == ERROR_NOT_FOUND, "got %ld\n", err ); + + memset( &entry_row, 0xcc, sizeof(entry_row) ); + entry_row.Family = row->Family; + entry_row.InterfaceLuid.Value = 0; + entry_row.InterfaceIndex = row->InterfaceIndex; + err = GetIpInterfaceEntry( &entry_row ); + ok( !err, "got %ld\n", err ); + ok( entry_row.Family == row->Family, "got %d, expected %d.\n", entry_row.Family, row->Family ); + ok( entry_row.InterfaceLuid.Value == row->InterfaceLuid.Value, "got %#I64x, expected %#I64x.\n", + entry_row.InterfaceLuid.Value, row->InterfaceLuid.Value ); + ok( entry_row.InterfaceIndex == row->InterfaceIndex, "got %lu, expected %lu.\n", + entry_row.InterfaceIndex, row->InterfaceIndex ); + ok( entry_row.BaseReachableTime == row->BaseReachableTime, "got %lu, expected %lu.\n", + entry_row.BaseReachableTime, row->BaseReachableTime ); } ok( loopback_found, "loopback not found.\n" ); FreeMibTable( table ); @@ -3205,7 +3250,7 @@ START_TEST(iphlpapi) test_NotifyUnicastIpAddressChange(); test_ConvertGuidToString(); test_compartments(); - test_GetIpInterfaceTable(); + test_GetIpInterface(); freeIPHlpApi(); }
diff --git a/include/netioapi.h b/include/netioapi.h index 9a9cff8ca79..09824f946e4 100644 --- a/include/netioapi.h +++ b/include/netioapi.h @@ -276,6 +276,7 @@ IPHLPAPI_DLL_LINKAGE DWORD WINAPI GetIfTable2(MIB_IF_TABLE2**); IPHLPAPI_DLL_LINKAGE DWORD WINAPI GetIfTable2Ex(MIB_IF_TABLE_LEVEL,MIB_IF_TABLE2**); IPHLPAPI_DLL_LINKAGE DWORD WINAPI GetIpForwardEntry2(MIB_IPFORWARD_ROW2*); IPHLPAPI_DLL_LINKAGE DWORD WINAPI GetIpForwardTable2(ADDRESS_FAMILY,MIB_IPFORWARD_TABLE2**); +IPHLPAPI_DLL_LINKAGE DWORD WINAPI GetIpInterfaceEntry(MIB_IPINTERFACE_ROW*); IPHLPAPI_DLL_LINKAGE DWORD WINAPI GetIpInterfaceTable(ADDRESS_FAMILY,MIB_IPINTERFACE_TABLE**); IPHLPAPI_DLL_LINKAGE DWORD WINAPI GetIpNetEntry2(MIB_IPNET_ROW2*); IPHLPAPI_DLL_LINKAGE DWORD WINAPI GetIpNetTable2(ADDRESS_FAMILY,MIB_IPNET_TABLE2**);
v2: - use NSI_IP_INTERFACE_TABLE instead of 7 in test; - fix comment text.
This merge request was approved by Huw Davies.