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..be43d32557d 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 IP interface table entry correspons 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..56cf8508e7b 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, 7, (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**);
Farlight 84 now depends on GetIpInterfaceEntry (and Icmp6ParseReplies) presence (as originally discovered by Etaash Mathamsetty here: https://github.com/ValveSoftware/Proton/issues/6717#issuecomment-3168585829).
While isn't so far spotted using these functions it checks its presence and apparently some part of the SDK's component initialization is skipped if those are absent.
While empty stubs might be enough in this specific case it probably won't hurt to implement that properly. This MR concerns GetIpInterfaceEntry (while I am likely going to also look at iphlpapi Icmp6 ping implementation).
Huw Davies (@huw) commented about dlls/nsiproxy.sys/ip.c:
- 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 IP interface table entry correspons to link local IPV6 address, while reported info for loopback
```suggestion:-0+0 /* Info in the IP interface table entry corresponds to link local IPv6 address, while reported info for loopback ```
Huw Davies (@huw) commented about dlls/nsi/tests/nsi.c:
+{
- 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, 7, (void **)&key_tbl, sizeof(*key_tbl),
```suggestion:-0+0 err = NsiAllocateAndGetTable( 1, mod, NSI_IP_INTERFACE_TABLE, (void **)&key_tbl, sizeof(*key_tbl), ```